##// END OF EJS Templates
ui: fixed branding look&feel as per discussions.
dan -
r3543:692134f0 default
parent child
Show More
@@ -1,744 +1,743
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib.utils2 import md5
26 from rhodecode.lib.utils2 import md5
27 from rhodecode.model.db import RhodeCodeUi
27 from rhodecode.model.db import RhodeCodeUi
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 from rhodecode.tests import assert_session_flash
30 from rhodecode.tests import assert_session_flash
31 from rhodecode.tests.utils import AssertResponse
31 from rhodecode.tests.utils import AssertResponse
32
32
33
33
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
35
35
36
36
37 def route_path(name, params=None, **kwargs):
37 def route_path(name, params=None, **kwargs):
38 import urllib
38 import urllib
39 from rhodecode.apps._base import ADMIN_PREFIX
39 from rhodecode.apps._base import ADMIN_PREFIX
40
40
41 base_url = {
41 base_url = {
42
42
43 'admin_settings':
43 'admin_settings':
44 ADMIN_PREFIX +'/settings',
44 ADMIN_PREFIX +'/settings',
45 'admin_settings_update':
45 'admin_settings_update':
46 ADMIN_PREFIX + '/settings/update',
46 ADMIN_PREFIX + '/settings/update',
47 'admin_settings_global':
47 'admin_settings_global':
48 ADMIN_PREFIX + '/settings/global',
48 ADMIN_PREFIX + '/settings/global',
49 'admin_settings_global_update':
49 'admin_settings_global_update':
50 ADMIN_PREFIX + '/settings/global/update',
50 ADMIN_PREFIX + '/settings/global/update',
51 'admin_settings_vcs':
51 'admin_settings_vcs':
52 ADMIN_PREFIX + '/settings/vcs',
52 ADMIN_PREFIX + '/settings/vcs',
53 'admin_settings_vcs_update':
53 'admin_settings_vcs_update':
54 ADMIN_PREFIX + '/settings/vcs/update',
54 ADMIN_PREFIX + '/settings/vcs/update',
55 'admin_settings_vcs_svn_pattern_delete':
55 'admin_settings_vcs_svn_pattern_delete':
56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
57 'admin_settings_mapping':
57 'admin_settings_mapping':
58 ADMIN_PREFIX + '/settings/mapping',
58 ADMIN_PREFIX + '/settings/mapping',
59 'admin_settings_mapping_update':
59 'admin_settings_mapping_update':
60 ADMIN_PREFIX + '/settings/mapping/update',
60 ADMIN_PREFIX + '/settings/mapping/update',
61 'admin_settings_visual':
61 'admin_settings_visual':
62 ADMIN_PREFIX + '/settings/visual',
62 ADMIN_PREFIX + '/settings/visual',
63 'admin_settings_visual_update':
63 'admin_settings_visual_update':
64 ADMIN_PREFIX + '/settings/visual/update',
64 ADMIN_PREFIX + '/settings/visual/update',
65 'admin_settings_issuetracker':
65 'admin_settings_issuetracker':
66 ADMIN_PREFIX + '/settings/issue-tracker',
66 ADMIN_PREFIX + '/settings/issue-tracker',
67 'admin_settings_issuetracker_update':
67 'admin_settings_issuetracker_update':
68 ADMIN_PREFIX + '/settings/issue-tracker/update',
68 ADMIN_PREFIX + '/settings/issue-tracker/update',
69 'admin_settings_issuetracker_test':
69 'admin_settings_issuetracker_test':
70 ADMIN_PREFIX + '/settings/issue-tracker/test',
70 ADMIN_PREFIX + '/settings/issue-tracker/test',
71 'admin_settings_issuetracker_delete':
71 'admin_settings_issuetracker_delete':
72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
73 'admin_settings_email':
73 'admin_settings_email':
74 ADMIN_PREFIX + '/settings/email',
74 ADMIN_PREFIX + '/settings/email',
75 'admin_settings_email_update':
75 'admin_settings_email_update':
76 ADMIN_PREFIX + '/settings/email/update',
76 ADMIN_PREFIX + '/settings/email/update',
77 'admin_settings_hooks':
77 'admin_settings_hooks':
78 ADMIN_PREFIX + '/settings/hooks',
78 ADMIN_PREFIX + '/settings/hooks',
79 'admin_settings_hooks_update':
79 'admin_settings_hooks_update':
80 ADMIN_PREFIX + '/settings/hooks/update',
80 ADMIN_PREFIX + '/settings/hooks/update',
81 'admin_settings_hooks_delete':
81 'admin_settings_hooks_delete':
82 ADMIN_PREFIX + '/settings/hooks/delete',
82 ADMIN_PREFIX + '/settings/hooks/delete',
83 'admin_settings_search':
83 'admin_settings_search':
84 ADMIN_PREFIX + '/settings/search',
84 ADMIN_PREFIX + '/settings/search',
85 'admin_settings_labs':
85 'admin_settings_labs':
86 ADMIN_PREFIX + '/settings/labs',
86 ADMIN_PREFIX + '/settings/labs',
87 'admin_settings_labs_update':
87 'admin_settings_labs_update':
88 ADMIN_PREFIX + '/settings/labs/update',
88 ADMIN_PREFIX + '/settings/labs/update',
89
89
90 'admin_settings_sessions':
90 'admin_settings_sessions':
91 ADMIN_PREFIX + '/settings/sessions',
91 ADMIN_PREFIX + '/settings/sessions',
92 'admin_settings_sessions_cleanup':
92 'admin_settings_sessions_cleanup':
93 ADMIN_PREFIX + '/settings/sessions/cleanup',
93 ADMIN_PREFIX + '/settings/sessions/cleanup',
94 'admin_settings_system':
94 'admin_settings_system':
95 ADMIN_PREFIX + '/settings/system',
95 ADMIN_PREFIX + '/settings/system',
96 'admin_settings_system_update':
96 'admin_settings_system_update':
97 ADMIN_PREFIX + '/settings/system/updates',
97 ADMIN_PREFIX + '/settings/system/updates',
98 'admin_settings_open_source':
98 'admin_settings_open_source':
99 ADMIN_PREFIX + '/settings/open_source',
99 ADMIN_PREFIX + '/settings/open_source',
100
100
101
101
102 }[name].format(**kwargs)
102 }[name].format(**kwargs)
103
103
104 if params:
104 if params:
105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
106 return base_url
106 return base_url
107
107
108
108
109 @pytest.mark.usefixtures('autologin_user', 'app')
109 @pytest.mark.usefixtures('autologin_user', 'app')
110 class TestAdminSettingsController(object):
110 class TestAdminSettingsController(object):
111
111
112 @pytest.mark.parametrize('urlname', [
112 @pytest.mark.parametrize('urlname', [
113 'admin_settings_vcs',
113 'admin_settings_vcs',
114 'admin_settings_mapping',
114 'admin_settings_mapping',
115 'admin_settings_global',
115 'admin_settings_global',
116 'admin_settings_visual',
116 'admin_settings_visual',
117 'admin_settings_email',
117 'admin_settings_email',
118 'admin_settings_hooks',
118 'admin_settings_hooks',
119 'admin_settings_search',
119 'admin_settings_search',
120 ])
120 ])
121 def test_simple_get(self, urlname):
121 def test_simple_get(self, urlname):
122 self.app.get(route_path(urlname))
122 self.app.get(route_path(urlname))
123
123
124 def test_create_custom_hook(self, csrf_token):
124 def test_create_custom_hook(self, csrf_token):
125 response = self.app.post(
125 response = self.app.post(
126 route_path('admin_settings_hooks_update'),
126 route_path('admin_settings_hooks_update'),
127 params={
127 params={
128 'new_hook_ui_key': 'test_hooks_1',
128 'new_hook_ui_key': 'test_hooks_1',
129 'new_hook_ui_value': 'cd /tmp',
129 'new_hook_ui_value': 'cd /tmp',
130 'csrf_token': csrf_token})
130 'csrf_token': csrf_token})
131
131
132 response = response.follow()
132 response = response.follow()
133 response.mustcontain('test_hooks_1')
133 response.mustcontain('test_hooks_1')
134 response.mustcontain('cd /tmp')
134 response.mustcontain('cd /tmp')
135
135
136 def test_create_custom_hook_delete(self, csrf_token):
136 def test_create_custom_hook_delete(self, csrf_token):
137 response = self.app.post(
137 response = self.app.post(
138 route_path('admin_settings_hooks_update'),
138 route_path('admin_settings_hooks_update'),
139 params={
139 params={
140 'new_hook_ui_key': 'test_hooks_2',
140 'new_hook_ui_key': 'test_hooks_2',
141 'new_hook_ui_value': 'cd /tmp2',
141 'new_hook_ui_value': 'cd /tmp2',
142 'csrf_token': csrf_token})
142 'csrf_token': csrf_token})
143
143
144 response = response.follow()
144 response = response.follow()
145 response.mustcontain('test_hooks_2')
145 response.mustcontain('test_hooks_2')
146 response.mustcontain('cd /tmp2')
146 response.mustcontain('cd /tmp2')
147
147
148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
149
149
150 # delete
150 # delete
151 self.app.post(
151 self.app.post(
152 route_path('admin_settings_hooks_delete'),
152 route_path('admin_settings_hooks_delete'),
153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
154 response = self.app.get(route_path('admin_settings_hooks'))
154 response = self.app.get(route_path('admin_settings_hooks'))
155 response.mustcontain(no=['test_hooks_2'])
155 response.mustcontain(no=['test_hooks_2'])
156 response.mustcontain(no=['cd /tmp2'])
156 response.mustcontain(no=['cd /tmp2'])
157
157
158
158
159 @pytest.mark.usefixtures('autologin_user', 'app')
159 @pytest.mark.usefixtures('autologin_user', 'app')
160 class TestAdminSettingsGlobal(object):
160 class TestAdminSettingsGlobal(object):
161
161
162 def test_pre_post_code_code_active(self, csrf_token):
162 def test_pre_post_code_code_active(self, csrf_token):
163 pre_code = 'rc-pre-code-187652122'
163 pre_code = 'rc-pre-code-187652122'
164 post_code = 'rc-postcode-98165231'
164 post_code = 'rc-postcode-98165231'
165
165
166 response = self.post_and_verify_settings({
166 response = self.post_and_verify_settings({
167 'rhodecode_pre_code': pre_code,
167 'rhodecode_pre_code': pre_code,
168 'rhodecode_post_code': post_code,
168 'rhodecode_post_code': post_code,
169 'csrf_token': csrf_token,
169 'csrf_token': csrf_token,
170 })
170 })
171
171
172 response = response.follow()
172 response = response.follow()
173 response.mustcontain(pre_code, post_code)
173 response.mustcontain(pre_code, post_code)
174
174
175 def test_pre_post_code_code_inactive(self, csrf_token):
175 def test_pre_post_code_code_inactive(self, csrf_token):
176 pre_code = 'rc-pre-code-187652122'
176 pre_code = 'rc-pre-code-187652122'
177 post_code = 'rc-postcode-98165231'
177 post_code = 'rc-postcode-98165231'
178 response = self.post_and_verify_settings({
178 response = self.post_and_verify_settings({
179 'rhodecode_pre_code': '',
179 'rhodecode_pre_code': '',
180 'rhodecode_post_code': '',
180 'rhodecode_post_code': '',
181 'csrf_token': csrf_token,
181 'csrf_token': csrf_token,
182 })
182 })
183
183
184 response = response.follow()
184 response = response.follow()
185 response.mustcontain(no=[pre_code, post_code])
185 response.mustcontain(no=[pre_code, post_code])
186
186
187 def test_captcha_activate(self, csrf_token):
187 def test_captcha_activate(self, csrf_token):
188 self.post_and_verify_settings({
188 self.post_and_verify_settings({
189 'rhodecode_captcha_private_key': '1234567890',
189 'rhodecode_captcha_private_key': '1234567890',
190 'rhodecode_captcha_public_key': '1234567890',
190 'rhodecode_captcha_public_key': '1234567890',
191 'csrf_token': csrf_token,
191 'csrf_token': csrf_token,
192 })
192 })
193
193
194 response = self.app.get(ADMIN_PREFIX + '/register')
194 response = self.app.get(ADMIN_PREFIX + '/register')
195 response.mustcontain('captcha')
195 response.mustcontain('captcha')
196
196
197 def test_captcha_deactivate(self, csrf_token):
197 def test_captcha_deactivate(self, csrf_token):
198 self.post_and_verify_settings({
198 self.post_and_verify_settings({
199 'rhodecode_captcha_private_key': '',
199 'rhodecode_captcha_private_key': '',
200 'rhodecode_captcha_public_key': '1234567890',
200 'rhodecode_captcha_public_key': '1234567890',
201 'csrf_token': csrf_token,
201 'csrf_token': csrf_token,
202 })
202 })
203
203
204 response = self.app.get(ADMIN_PREFIX + '/register')
204 response = self.app.get(ADMIN_PREFIX + '/register')
205 response.mustcontain(no=['captcha'])
205 response.mustcontain(no=['captcha'])
206
206
207 def test_title_change(self, csrf_token):
207 def test_title_change(self, csrf_token):
208 old_title = 'RhodeCode'
208 old_title = 'RhodeCode'
209
209
210 for new_title in ['Changed', 'Żółwik', old_title]:
210 for new_title in ['Changed', 'Żółwik', old_title]:
211 response = self.post_and_verify_settings({
211 response = self.post_and_verify_settings({
212 'rhodecode_title': new_title,
212 'rhodecode_title': new_title,
213 'csrf_token': csrf_token,
213 'csrf_token': csrf_token,
214 })
214 })
215
215
216 response = response.follow()
216 response = response.follow()
217 response.mustcontain(
217 response.mustcontain(new_title)
218 """<div class="branding">- %s</div>""" % new_title)
219
218
220 def post_and_verify_settings(self, settings):
219 def post_and_verify_settings(self, settings):
221 old_title = 'RhodeCode'
220 old_title = 'RhodeCode'
222 old_realm = 'RhodeCode authentication'
221 old_realm = 'RhodeCode authentication'
223 params = {
222 params = {
224 'rhodecode_title': old_title,
223 'rhodecode_title': old_title,
225 'rhodecode_realm': old_realm,
224 'rhodecode_realm': old_realm,
226 'rhodecode_pre_code': '',
225 'rhodecode_pre_code': '',
227 'rhodecode_post_code': '',
226 'rhodecode_post_code': '',
228 'rhodecode_captcha_private_key': '',
227 'rhodecode_captcha_private_key': '',
229 'rhodecode_captcha_public_key': '',
228 'rhodecode_captcha_public_key': '',
230 'rhodecode_create_personal_repo_group': False,
229 'rhodecode_create_personal_repo_group': False,
231 'rhodecode_personal_repo_group_pattern': '${username}',
230 'rhodecode_personal_repo_group_pattern': '${username}',
232 }
231 }
233 params.update(settings)
232 params.update(settings)
234 response = self.app.post(
233 response = self.app.post(
235 route_path('admin_settings_global_update'), params=params)
234 route_path('admin_settings_global_update'), params=params)
236
235
237 assert_session_flash(response, 'Updated application settings')
236 assert_session_flash(response, 'Updated application settings')
238 app_settings = SettingsModel().get_all_settings()
237 app_settings = SettingsModel().get_all_settings()
239 del settings['csrf_token']
238 del settings['csrf_token']
240 for key, value in settings.iteritems():
239 for key, value in settings.iteritems():
241 assert app_settings[key] == value.decode('utf-8')
240 assert app_settings[key] == value.decode('utf-8')
242
241
243 return response
242 return response
244
243
245
244
246 @pytest.mark.usefixtures('autologin_user', 'app')
245 @pytest.mark.usefixtures('autologin_user', 'app')
247 class TestAdminSettingsVcs(object):
246 class TestAdminSettingsVcs(object):
248
247
249 def test_contains_svn_default_patterns(self):
248 def test_contains_svn_default_patterns(self):
250 response = self.app.get(route_path('admin_settings_vcs'))
249 response = self.app.get(route_path('admin_settings_vcs'))
251 expected_patterns = [
250 expected_patterns = [
252 '/trunk',
251 '/trunk',
253 '/branches/*',
252 '/branches/*',
254 '/tags/*',
253 '/tags/*',
255 ]
254 ]
256 for pattern in expected_patterns:
255 for pattern in expected_patterns:
257 response.mustcontain(pattern)
256 response.mustcontain(pattern)
258
257
259 def test_add_new_svn_branch_and_tag_pattern(
258 def test_add_new_svn_branch_and_tag_pattern(
260 self, backend_svn, form_defaults, disable_sql_cache,
259 self, backend_svn, form_defaults, disable_sql_cache,
261 csrf_token):
260 csrf_token):
262 form_defaults.update({
261 form_defaults.update({
263 'new_svn_branch': '/exp/branches/*',
262 'new_svn_branch': '/exp/branches/*',
264 'new_svn_tag': '/important_tags/*',
263 'new_svn_tag': '/important_tags/*',
265 'csrf_token': csrf_token,
264 'csrf_token': csrf_token,
266 })
265 })
267
266
268 response = self.app.post(
267 response = self.app.post(
269 route_path('admin_settings_vcs_update'),
268 route_path('admin_settings_vcs_update'),
270 params=form_defaults, status=302)
269 params=form_defaults, status=302)
271 response = response.follow()
270 response = response.follow()
272
271
273 # Expect to find the new values on the page
272 # Expect to find the new values on the page
274 response.mustcontain('/exp/branches/*')
273 response.mustcontain('/exp/branches/*')
275 response.mustcontain('/important_tags/*')
274 response.mustcontain('/important_tags/*')
276
275
277 # Expect that those patterns are used to match branches and tags now
276 # Expect that those patterns are used to match branches and tags now
278 repo = backend_svn['svn-simple-layout'].scm_instance()
277 repo = backend_svn['svn-simple-layout'].scm_instance()
279 assert 'exp/branches/exp-sphinx-docs' in repo.branches
278 assert 'exp/branches/exp-sphinx-docs' in repo.branches
280 assert 'important_tags/v0.5' in repo.tags
279 assert 'important_tags/v0.5' in repo.tags
281
280
282 def test_add_same_svn_value_twice_shows_an_error_message(
281 def test_add_same_svn_value_twice_shows_an_error_message(
283 self, form_defaults, csrf_token, settings_util):
282 self, form_defaults, csrf_token, settings_util):
284 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
283 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
285 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
284 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
286
285
287 response = self.app.post(
286 response = self.app.post(
288 route_path('admin_settings_vcs_update'),
287 route_path('admin_settings_vcs_update'),
289 params={
288 params={
290 'paths_root_path': form_defaults['paths_root_path'],
289 'paths_root_path': form_defaults['paths_root_path'],
291 'new_svn_branch': '/test',
290 'new_svn_branch': '/test',
292 'new_svn_tag': '/test',
291 'new_svn_tag': '/test',
293 'csrf_token': csrf_token,
292 'csrf_token': csrf_token,
294 },
293 },
295 status=200)
294 status=200)
296
295
297 response.mustcontain("Pattern already exists")
296 response.mustcontain("Pattern already exists")
298 response.mustcontain("Some form inputs contain invalid data.")
297 response.mustcontain("Some form inputs contain invalid data.")
299
298
300 @pytest.mark.parametrize('section', [
299 @pytest.mark.parametrize('section', [
301 'vcs_svn_branch',
300 'vcs_svn_branch',
302 'vcs_svn_tag',
301 'vcs_svn_tag',
303 ])
302 ])
304 def test_delete_svn_patterns(
303 def test_delete_svn_patterns(
305 self, section, csrf_token, settings_util):
304 self, section, csrf_token, settings_util):
306 setting = settings_util.create_rhodecode_ui(
305 setting = settings_util.create_rhodecode_ui(
307 section, '/test_delete', cleanup=False)
306 section, '/test_delete', cleanup=False)
308
307
309 self.app.post(
308 self.app.post(
310 route_path('admin_settings_vcs_svn_pattern_delete'),
309 route_path('admin_settings_vcs_svn_pattern_delete'),
311 params={
310 params={
312 'delete_svn_pattern': setting.ui_id,
311 'delete_svn_pattern': setting.ui_id,
313 'csrf_token': csrf_token},
312 'csrf_token': csrf_token},
314 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
313 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
315
314
316 @pytest.mark.parametrize('section', [
315 @pytest.mark.parametrize('section', [
317 'vcs_svn_branch',
316 'vcs_svn_branch',
318 'vcs_svn_tag',
317 'vcs_svn_tag',
319 ])
318 ])
320 def test_delete_svn_patterns_raises_404_when_no_xhr(
319 def test_delete_svn_patterns_raises_404_when_no_xhr(
321 self, section, csrf_token, settings_util):
320 self, section, csrf_token, settings_util):
322 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
321 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
323
322
324 self.app.post(
323 self.app.post(
325 route_path('admin_settings_vcs_svn_pattern_delete'),
324 route_path('admin_settings_vcs_svn_pattern_delete'),
326 params={
325 params={
327 'delete_svn_pattern': setting.ui_id,
326 'delete_svn_pattern': setting.ui_id,
328 'csrf_token': csrf_token},
327 'csrf_token': csrf_token},
329 status=404)
328 status=404)
330
329
331 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
330 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
332 form_defaults.update({
331 form_defaults.update({
333 'csrf_token': csrf_token,
332 'csrf_token': csrf_token,
334 'extensions_hgsubversion': 'True',
333 'extensions_hgsubversion': 'True',
335 })
334 })
336 response = self.app.post(
335 response = self.app.post(
337 route_path('admin_settings_vcs_update'),
336 route_path('admin_settings_vcs_update'),
338 params=form_defaults,
337 params=form_defaults,
339 status=302)
338 status=302)
340
339
341 response = response.follow()
340 response = response.follow()
342 extensions_input = (
341 extensions_input = (
343 '<input id="extensions_hgsubversion" '
342 '<input id="extensions_hgsubversion" '
344 'name="extensions_hgsubversion" type="checkbox" '
343 'name="extensions_hgsubversion" type="checkbox" '
345 'value="True" checked="checked" />')
344 'value="True" checked="checked" />')
346 response.mustcontain(extensions_input)
345 response.mustcontain(extensions_input)
347
346
348 def test_extensions_hgevolve(self, form_defaults, csrf_token):
347 def test_extensions_hgevolve(self, form_defaults, csrf_token):
349 form_defaults.update({
348 form_defaults.update({
350 'csrf_token': csrf_token,
349 'csrf_token': csrf_token,
351 'extensions_evolve': 'True',
350 'extensions_evolve': 'True',
352 })
351 })
353 response = self.app.post(
352 response = self.app.post(
354 route_path('admin_settings_vcs_update'),
353 route_path('admin_settings_vcs_update'),
355 params=form_defaults,
354 params=form_defaults,
356 status=302)
355 status=302)
357
356
358 response = response.follow()
357 response = response.follow()
359 extensions_input = (
358 extensions_input = (
360 '<input id="extensions_evolve" '
359 '<input id="extensions_evolve" '
361 'name="extensions_evolve" type="checkbox" '
360 'name="extensions_evolve" type="checkbox" '
362 'value="True" checked="checked" />')
361 'value="True" checked="checked" />')
363 response.mustcontain(extensions_input)
362 response.mustcontain(extensions_input)
364
363
365 def test_has_a_section_for_pull_request_settings(self):
364 def test_has_a_section_for_pull_request_settings(self):
366 response = self.app.get(route_path('admin_settings_vcs'))
365 response = self.app.get(route_path('admin_settings_vcs'))
367 response.mustcontain('Pull Request Settings')
366 response.mustcontain('Pull Request Settings')
368
367
369 def test_has_an_input_for_invalidation_of_inline_comments(self):
368 def test_has_an_input_for_invalidation_of_inline_comments(self):
370 response = self.app.get(route_path('admin_settings_vcs'))
369 response = self.app.get(route_path('admin_settings_vcs'))
371 assert_response = AssertResponse(response)
370 assert_response = AssertResponse(response)
372 assert_response.one_element_exists(
371 assert_response.one_element_exists(
373 '[name=rhodecode_use_outdated_comments]')
372 '[name=rhodecode_use_outdated_comments]')
374
373
375 @pytest.mark.parametrize('new_value', [True, False])
374 @pytest.mark.parametrize('new_value', [True, False])
376 def test_allows_to_change_invalidation_of_inline_comments(
375 def test_allows_to_change_invalidation_of_inline_comments(
377 self, form_defaults, csrf_token, new_value):
376 self, form_defaults, csrf_token, new_value):
378 setting_key = 'use_outdated_comments'
377 setting_key = 'use_outdated_comments'
379 setting = SettingsModel().create_or_update_setting(
378 setting = SettingsModel().create_or_update_setting(
380 setting_key, not new_value, 'bool')
379 setting_key, not new_value, 'bool')
381 Session().add(setting)
380 Session().add(setting)
382 Session().commit()
381 Session().commit()
383
382
384 form_defaults.update({
383 form_defaults.update({
385 'csrf_token': csrf_token,
384 'csrf_token': csrf_token,
386 'rhodecode_use_outdated_comments': str(new_value),
385 'rhodecode_use_outdated_comments': str(new_value),
387 })
386 })
388 response = self.app.post(
387 response = self.app.post(
389 route_path('admin_settings_vcs_update'),
388 route_path('admin_settings_vcs_update'),
390 params=form_defaults,
389 params=form_defaults,
391 status=302)
390 status=302)
392 response = response.follow()
391 response = response.follow()
393 setting = SettingsModel().get_setting_by_name(setting_key)
392 setting = SettingsModel().get_setting_by_name(setting_key)
394 assert setting.app_settings_value is new_value
393 assert setting.app_settings_value is new_value
395
394
396 @pytest.mark.parametrize('new_value', [True, False])
395 @pytest.mark.parametrize('new_value', [True, False])
397 def test_allows_to_change_hg_rebase_merge_strategy(
396 def test_allows_to_change_hg_rebase_merge_strategy(
398 self, form_defaults, csrf_token, new_value):
397 self, form_defaults, csrf_token, new_value):
399 setting_key = 'hg_use_rebase_for_merging'
398 setting_key = 'hg_use_rebase_for_merging'
400
399
401 form_defaults.update({
400 form_defaults.update({
402 'csrf_token': csrf_token,
401 'csrf_token': csrf_token,
403 'rhodecode_' + setting_key: str(new_value),
402 'rhodecode_' + setting_key: str(new_value),
404 })
403 })
405
404
406 with mock.patch.dict(
405 with mock.patch.dict(
407 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
406 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
408 self.app.post(
407 self.app.post(
409 route_path('admin_settings_vcs_update'),
408 route_path('admin_settings_vcs_update'),
410 params=form_defaults,
409 params=form_defaults,
411 status=302)
410 status=302)
412
411
413 setting = SettingsModel().get_setting_by_name(setting_key)
412 setting = SettingsModel().get_setting_by_name(setting_key)
414 assert setting.app_settings_value is new_value
413 assert setting.app_settings_value is new_value
415
414
416 @pytest.fixture
415 @pytest.fixture
417 def disable_sql_cache(self, request):
416 def disable_sql_cache(self, request):
418 patcher = mock.patch(
417 patcher = mock.patch(
419 'rhodecode.lib.caching_query.FromCache.process_query')
418 'rhodecode.lib.caching_query.FromCache.process_query')
420 request.addfinalizer(patcher.stop)
419 request.addfinalizer(patcher.stop)
421 patcher.start()
420 patcher.start()
422
421
423 @pytest.fixture
422 @pytest.fixture
424 def form_defaults(self):
423 def form_defaults(self):
425 from rhodecode.apps.admin.views.settings import AdminSettingsView
424 from rhodecode.apps.admin.views.settings import AdminSettingsView
426 return AdminSettingsView._form_defaults()
425 return AdminSettingsView._form_defaults()
427
426
428 # TODO: johbo: What we really want is to checkpoint before a test run and
427 # TODO: johbo: What we really want is to checkpoint before a test run and
429 # reset the session afterwards.
428 # reset the session afterwards.
430 @pytest.fixture(scope='class', autouse=True)
429 @pytest.fixture(scope='class', autouse=True)
431 def cleanup_settings(self, request, baseapp):
430 def cleanup_settings(self, request, baseapp):
432 ui_id = RhodeCodeUi.ui_id
431 ui_id = RhodeCodeUi.ui_id
433 original_ids = list(
432 original_ids = list(
434 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
433 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
435
434
436 @request.addfinalizer
435 @request.addfinalizer
437 def cleanup():
436 def cleanup():
438 RhodeCodeUi.query().filter(
437 RhodeCodeUi.query().filter(
439 ui_id.notin_(original_ids)).delete(False)
438 ui_id.notin_(original_ids)).delete(False)
440
439
441
440
442 @pytest.mark.usefixtures('autologin_user', 'app')
441 @pytest.mark.usefixtures('autologin_user', 'app')
443 class TestLabsSettings(object):
442 class TestLabsSettings(object):
444 def test_get_settings_page_disabled(self):
443 def test_get_settings_page_disabled(self):
445 with mock.patch.dict(
444 with mock.patch.dict(
446 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
445 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
447
446
448 response = self.app.get(
447 response = self.app.get(
449 route_path('admin_settings_labs'), status=302)
448 route_path('admin_settings_labs'), status=302)
450
449
451 assert response.location.endswith(route_path('admin_settings'))
450 assert response.location.endswith(route_path('admin_settings'))
452
451
453 def test_get_settings_page_enabled(self):
452 def test_get_settings_page_enabled(self):
454 from rhodecode.apps.admin.views import settings
453 from rhodecode.apps.admin.views import settings
455 lab_settings = [
454 lab_settings = [
456 settings.LabSetting(
455 settings.LabSetting(
457 key='rhodecode_bool',
456 key='rhodecode_bool',
458 type='bool',
457 type='bool',
459 group='bool group',
458 group='bool group',
460 label='bool label',
459 label='bool label',
461 help='bool help'
460 help='bool help'
462 ),
461 ),
463 settings.LabSetting(
462 settings.LabSetting(
464 key='rhodecode_text',
463 key='rhodecode_text',
465 type='unicode',
464 type='unicode',
466 group='text group',
465 group='text group',
467 label='text label',
466 label='text label',
468 help='text help'
467 help='text help'
469 ),
468 ),
470 ]
469 ]
471 with mock.patch.dict(rhodecode.CONFIG,
470 with mock.patch.dict(rhodecode.CONFIG,
472 {'labs_settings_active': 'true'}):
471 {'labs_settings_active': 'true'}):
473 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
472 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
474 response = self.app.get(route_path('admin_settings_labs'))
473 response = self.app.get(route_path('admin_settings_labs'))
475
474
476 assert '<label>bool group:</label>' in response
475 assert '<label>bool group:</label>' in response
477 assert '<label for="rhodecode_bool">bool label</label>' in response
476 assert '<label for="rhodecode_bool">bool label</label>' in response
478 assert '<p class="help-block">bool help</p>' in response
477 assert '<p class="help-block">bool help</p>' in response
479 assert 'name="rhodecode_bool" type="checkbox"' in response
478 assert 'name="rhodecode_bool" type="checkbox"' in response
480
479
481 assert '<label>text group:</label>' in response
480 assert '<label>text group:</label>' in response
482 assert '<label for="rhodecode_text">text label</label>' in response
481 assert '<label for="rhodecode_text">text label</label>' in response
483 assert '<p class="help-block">text help</p>' in response
482 assert '<p class="help-block">text help</p>' in response
484 assert 'name="rhodecode_text" size="60" type="text"' in response
483 assert 'name="rhodecode_text" size="60" type="text"' in response
485
484
486
485
487 @pytest.mark.usefixtures('app')
486 @pytest.mark.usefixtures('app')
488 class TestOpenSourceLicenses(object):
487 class TestOpenSourceLicenses(object):
489
488
490 def test_records_are_displayed(self, autologin_user):
489 def test_records_are_displayed(self, autologin_user):
491 sample_licenses = [
490 sample_licenses = [
492 {
491 {
493 "license": [
492 "license": [
494 {
493 {
495 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
494 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
496 "shortName": "bsdOriginal",
495 "shortName": "bsdOriginal",
497 "spdxId": "BSD-4-Clause",
496 "spdxId": "BSD-4-Clause",
498 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
497 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
499 }
498 }
500 ],
499 ],
501 "name": "python2.7-coverage-3.7.1"
500 "name": "python2.7-coverage-3.7.1"
502 },
501 },
503 {
502 {
504 "license": [
503 "license": [
505 {
504 {
506 "fullName": "MIT License",
505 "fullName": "MIT License",
507 "shortName": "mit",
506 "shortName": "mit",
508 "spdxId": "MIT",
507 "spdxId": "MIT",
509 "url": "http://spdx.org/licenses/MIT.html"
508 "url": "http://spdx.org/licenses/MIT.html"
510 }
509 }
511 ],
510 ],
512 "name": "python2.7-bootstrapped-pip-9.0.1"
511 "name": "python2.7-bootstrapped-pip-9.0.1"
513 },
512 },
514 ]
513 ]
515 read_licenses_patch = mock.patch(
514 read_licenses_patch = mock.patch(
516 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
515 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
517 return_value=sample_licenses)
516 return_value=sample_licenses)
518 with read_licenses_patch:
517 with read_licenses_patch:
519 response = self.app.get(
518 response = self.app.get(
520 route_path('admin_settings_open_source'), status=200)
519 route_path('admin_settings_open_source'), status=200)
521
520
522 assert_response = AssertResponse(response)
521 assert_response = AssertResponse(response)
523 assert_response.element_contains(
522 assert_response.element_contains(
524 '.panel-heading', 'Licenses of Third Party Packages')
523 '.panel-heading', 'Licenses of Third Party Packages')
525 for license_data in sample_licenses:
524 for license_data in sample_licenses:
526 response.mustcontain(license_data["license"][0]["spdxId"])
525 response.mustcontain(license_data["license"][0]["spdxId"])
527 assert_response.element_contains('.panel-body', license_data["name"])
526 assert_response.element_contains('.panel-body', license_data["name"])
528
527
529 def test_records_can_be_read(self, autologin_user):
528 def test_records_can_be_read(self, autologin_user):
530 response = self.app.get(
529 response = self.app.get(
531 route_path('admin_settings_open_source'), status=200)
530 route_path('admin_settings_open_source'), status=200)
532 assert_response = AssertResponse(response)
531 assert_response = AssertResponse(response)
533 assert_response.element_contains(
532 assert_response.element_contains(
534 '.panel-heading', 'Licenses of Third Party Packages')
533 '.panel-heading', 'Licenses of Third Party Packages')
535
534
536 def test_forbidden_when_normal_user(self, autologin_regular_user):
535 def test_forbidden_when_normal_user(self, autologin_regular_user):
537 self.app.get(
536 self.app.get(
538 route_path('admin_settings_open_source'), status=404)
537 route_path('admin_settings_open_source'), status=404)
539
538
540
539
541 @pytest.mark.usefixtures('app')
540 @pytest.mark.usefixtures('app')
542 class TestUserSessions(object):
541 class TestUserSessions(object):
543
542
544 def test_forbidden_when_normal_user(self, autologin_regular_user):
543 def test_forbidden_when_normal_user(self, autologin_regular_user):
545 self.app.get(route_path('admin_settings_sessions'), status=404)
544 self.app.get(route_path('admin_settings_sessions'), status=404)
546
545
547 def test_show_sessions_page(self, autologin_user):
546 def test_show_sessions_page(self, autologin_user):
548 response = self.app.get(route_path('admin_settings_sessions'), status=200)
547 response = self.app.get(route_path('admin_settings_sessions'), status=200)
549 response.mustcontain('file')
548 response.mustcontain('file')
550
549
551 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
550 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
552
551
553 post_data = {
552 post_data = {
554 'csrf_token': csrf_token,
553 'csrf_token': csrf_token,
555 'expire_days': '60'
554 'expire_days': '60'
556 }
555 }
557 response = self.app.post(
556 response = self.app.post(
558 route_path('admin_settings_sessions_cleanup'), params=post_data,
557 route_path('admin_settings_sessions_cleanup'), params=post_data,
559 status=302)
558 status=302)
560 assert_session_flash(response, 'Cleaned up old sessions')
559 assert_session_flash(response, 'Cleaned up old sessions')
561
560
562
561
563 @pytest.mark.usefixtures('app')
562 @pytest.mark.usefixtures('app')
564 class TestAdminSystemInfo(object):
563 class TestAdminSystemInfo(object):
565
564
566 def test_forbidden_when_normal_user(self, autologin_regular_user):
565 def test_forbidden_when_normal_user(self, autologin_regular_user):
567 self.app.get(route_path('admin_settings_system'), status=404)
566 self.app.get(route_path('admin_settings_system'), status=404)
568
567
569 def test_system_info_page(self, autologin_user):
568 def test_system_info_page(self, autologin_user):
570 response = self.app.get(route_path('admin_settings_system'))
569 response = self.app.get(route_path('admin_settings_system'))
571 response.mustcontain('RhodeCode Community Edition, version {}'.format(
570 response.mustcontain('RhodeCode Community Edition, version {}'.format(
572 rhodecode.__version__))
571 rhodecode.__version__))
573
572
574 def test_system_update_new_version(self, autologin_user):
573 def test_system_update_new_version(self, autologin_user):
575 update_data = {
574 update_data = {
576 'versions': [
575 'versions': [
577 {
576 {
578 'version': '100.3.1415926535',
577 'version': '100.3.1415926535',
579 'general': 'The latest version we are ever going to ship'
578 'general': 'The latest version we are ever going to ship'
580 },
579 },
581 {
580 {
582 'version': '0.0.0',
581 'version': '0.0.0',
583 'general': 'The first version we ever shipped'
582 'general': 'The first version we ever shipped'
584 }
583 }
585 ]
584 ]
586 }
585 }
587 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
586 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
588 response = self.app.get(route_path('admin_settings_system_update'))
587 response = self.app.get(route_path('admin_settings_system_update'))
589 response.mustcontain('A <b>new version</b> is available')
588 response.mustcontain('A <b>new version</b> is available')
590
589
591 def test_system_update_nothing_new(self, autologin_user):
590 def test_system_update_nothing_new(self, autologin_user):
592 update_data = {
591 update_data = {
593 'versions': [
592 'versions': [
594 {
593 {
595 'version': '0.0.0',
594 'version': '0.0.0',
596 'general': 'The first version we ever shipped'
595 'general': 'The first version we ever shipped'
597 }
596 }
598 ]
597 ]
599 }
598 }
600 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
599 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
601 response = self.app.get(route_path('admin_settings_system_update'))
600 response = self.app.get(route_path('admin_settings_system_update'))
602 response.mustcontain(