##// END OF EJS Templates
tests: fixes for commit pages changes.
marcink -
r3885:a66c5e67 default
parent child Browse files
Show More
@@ -1,320 +1,320 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.tests import TestController
24 24
25 25 from rhodecode.model.db import (
26 26 ChangesetComment, Notification, UserNotification)
27 27 from rhodecode.model.meta import Session
28 28 from rhodecode.lib import helpers as h
29 29
30 30
31 31 def route_path(name, params=None, **kwargs):
32 32 import urllib
33 33
34 34 base_url = {
35 35 'repo_commit': '/{repo_name}/changeset/{commit_id}',
36 36 'repo_commit_comment_create': '/{repo_name}/changeset/{commit_id}/comment/create',
37 37 'repo_commit_comment_preview': '/{repo_name}/changeset/{commit_id}/comment/preview',
38 38 'repo_commit_comment_delete': '/{repo_name}/changeset/{commit_id}/comment/{comment_id}/delete',
39 39 }[name].format(**kwargs)
40 40
41 41 if params:
42 42 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
43 43 return base_url
44 44
45 45
46 46 @pytest.mark.backends("git", "hg", "svn")
47 47 class TestRepoCommitCommentsView(TestController):
48 48
49 49 @pytest.fixture(autouse=True)
50 50 def prepare(self, request, baseapp):
51 51 for x in ChangesetComment.query().all():
52 52 Session().delete(x)
53 53 Session().commit()
54 54
55 55 for x in Notification.query().all():
56 56 Session().delete(x)
57 57 Session().commit()
58 58
59 59 request.addfinalizer(self.cleanup)
60 60
61 61 def cleanup(self):
62 62 for x in ChangesetComment.query().all():
63 63 Session().delete(x)
64 64 Session().commit()
65 65
66 66 for x in Notification.query().all():
67 67 Session().delete(x)
68 68 Session().commit()
69 69
70 70 @pytest.mark.parametrize('comment_type', ChangesetComment.COMMENT_TYPES)
71 71 def test_create(self, comment_type, backend):
72 72 self.log_user()
73 73 commit = backend.repo.get_commit('300')
74 74 commit_id = commit.raw_id
75 75 text = u'CommentOnCommit'
76 76
77 77 params = {'text': text, 'csrf_token': self.csrf_token,
78 78 'comment_type': comment_type}
79 79 self.app.post(
80 80 route_path('repo_commit_comment_create',
81 81 repo_name=backend.repo_name, commit_id=commit_id),
82 82 params=params)
83 83
84 84 response = self.app.get(
85 85 route_path('repo_commit',
86 86 repo_name=backend.repo_name, commit_id=commit_id))
87 87
88 88 # test DB
89 89 assert ChangesetComment.query().count() == 1
90 90 assert_comment_links(response, ChangesetComment.query().count(), 0)
91 91
92 92 assert Notification.query().count() == 1
93 93 assert ChangesetComment.query().count() == 1
94 94
95 95 notification = Notification.query().all()[0]
96 96
97 97 comment_id = ChangesetComment.query().first().comment_id
98 98 assert notification.type_ == Notification.TYPE_CHANGESET_COMMENT
99 99
100 100 author = notification.created_by_user.username_and_name
101 101 sbj = '{0} left a {1} on commit `{2}` in the {3} repository'.format(
102 102 author, comment_type, h.show_id(commit), backend.repo_name)
103 103 assert sbj == notification.subject
104 104
105 105 lnk = (u'/{0}/changeset/{1}#comment-{2}'.format(
106 106 backend.repo_name, commit_id, comment_id))
107 107 assert lnk in notification.body
108 108
109 109 @pytest.mark.parametrize('comment_type', ChangesetComment.COMMENT_TYPES)
110 110 def test_create_inline(self, comment_type, backend):
111 111 self.log_user()
112 112 commit = backend.repo.get_commit('300')
113 113 commit_id = commit.raw_id
114 114 text = u'CommentOnCommit'
115 115 f_path = 'vcs/web/simplevcs/views/repository.py'
116 116 line = 'n1'
117 117
118 118 params = {'text': text, 'f_path': f_path, 'line': line,
119 119 'comment_type': comment_type,
120 120 'csrf_token': self.csrf_token}
121 121
122 122 self.app.post(
123 123 route_path('repo_commit_comment_create',
124 124 repo_name=backend.repo_name, commit_id=commit_id),
125 125 params=params)
126 126
127 127 response = self.app.get(
128 128 route_path('repo_commit',
129 129 repo_name=backend.repo_name, commit_id=commit_id))
130 130
131 131 # test DB
132 132 assert ChangesetComment.query().count() == 1
133 133 assert_comment_links(response, 0, ChangesetComment.query().count())
134 134
135 135 if backend.alias == 'svn':
136 136 response.mustcontain(
137 137 '''data-f-path="vcs/commands/summary.py" '''
138 138 '''data-anchor-id="c-300-ad05457a43f8"'''
139 139 )
140 140 if backend.alias == 'git':
141 141 response.mustcontain(
142 142 '''data-f-path="vcs/backends/hg.py" '''
143 143 '''data-anchor-id="c-883e775e89ea-9c390eb52cd6"'''
144 144 )
145 145
146 146 if backend.alias == 'hg':
147 147 response.mustcontain(
148 148 '''data-f-path="vcs/backends/hg.py" '''
149 149 '''data-anchor-id="c-e58d85a3973b-9c390eb52cd6"'''
150 150 )
151 151
152 152 assert Notification.query().count() == 1
153 153 assert ChangesetComment.query().count() == 1
154 154
155 155 notification = Notification.query().all()[0]
156 156 comment = ChangesetComment.query().first()
157 157 assert notification.type_ == Notification.TYPE_CHANGESET_COMMENT
158 158
159 159 assert comment.revision == commit_id
160 160
161 161 author = notification.created_by_user.username_and_name
162 162 sbj = '{0} left a {1} on file `{2}` in commit `{3}` in the {4} repository'.format(
163 163 author, comment_type, f_path, h.show_id(commit), backend.repo_name)
164 164
165 165 assert sbj == notification.subject
166 166
167 167 lnk = (u'/{0}/changeset/{1}#comment-{2}'.format(
168 168 backend.repo_name, commit_id, comment.comment_id))
169 169 assert lnk in notification.body
170 170 assert 'on line n1' in notification.body
171 171
172 172 def test_create_with_mention(self, backend):
173 173 self.log_user()
174 174
175 175 commit_id = backend.repo.get_commit('300').raw_id
176 176 text = u'@test_regular check CommentOnCommit'
177 177
178 178 params = {'text': text, 'csrf_token': self.csrf_token}
179 179 self.app.post(
180 180 route_path('repo_commit_comment_create',
181 181 repo_name=backend.repo_name, commit_id=commit_id),
182 182 params=params)
183 183
184 184 response = self.app.get(
185 185 route_path('repo_commit',
186 186 repo_name=backend.repo_name, commit_id=commit_id))
187 187 # test DB
188 188 assert ChangesetComment.query().count() == 1
189 189 assert_comment_links(response, ChangesetComment.query().count(), 0)
190 190
191 191 notification = Notification.query().one()
192 192
193 193 assert len(notification.recipients) == 2
194 194 users = [x.username for x in notification.recipients]
195 195
196 196 # test_regular gets notification by @mention
197 197 assert sorted(users) == [u'test_admin', u'test_regular']
198 198
199 199 def test_create_with_status_change(self, backend):
200 200 self.log_user()
201 201 commit = backend.repo.get_commit('300')
202 202 commit_id = commit.raw_id
203 203 text = u'CommentOnCommit'
204 204 f_path = 'vcs/web/simplevcs/views/repository.py'
205 205 line = 'n1'
206 206
207 207 params = {'text': text, 'changeset_status': 'approved',
208 208 'csrf_token': self.csrf_token}
209 209
210 210 self.app.post(
211 211 route_path(
212 212 'repo_commit_comment_create',
213 213 repo_name=backend.repo_name, commit_id=commit_id),
214 214 params=params)
215 215
216 216 response = self.app.get(
217 217 route_path('repo_commit',
218 218 repo_name=backend.repo_name, commit_id=commit_id))
219 219
220 220 # test DB
221 221 assert ChangesetComment.query().count() == 1
222 222 assert_comment_links(response, ChangesetComment.query().count(), 0)
223 223
224 224 assert Notification.query().count() == 1
225 225 assert ChangesetComment.query().count() == 1
226 226
227 227 notification = Notification.query().all()[0]
228 228
229 229 comment_id = ChangesetComment.query().first().comment_id
230 230 assert notification.type_ == Notification.TYPE_CHANGESET_COMMENT
231 231
232 232 author = notification.created_by_user.username_and_name
233 233 sbj = '[status: Approved] {0} left a note on commit `{1}` in the {2} repository'.format(
234 234 author, h.show_id(commit), backend.repo_name)
235 235 assert sbj == notification.subject
236 236
237 237 lnk = (u'/{0}/changeset/{1}#comment-{2}'.format(
238 238 backend.repo_name, commit_id, comment_id))
239 239 assert lnk in notification.body
240 240
241 241 def test_delete(self, backend):
242 242 self.log_user()
243 243 commit_id = backend.repo.get_commit('300').raw_id
244 244 text = u'CommentOnCommit'
245 245
246 246 params = {'text': text, 'csrf_token': self.csrf_token}
247 247 self.app.post(
248 248 route_path(
249 249 'repo_commit_comment_create',
250 250 repo_name=backend.repo_name, commit_id=commit_id),
251 251 params=params)
252 252
253 253 comments = ChangesetComment.query().all()
254 254 assert len(comments) == 1
255 255 comment_id = comments[0].comment_id
256 256
257 257 self.app.post(
258 258 route_path('repo_commit_comment_delete',
259 259 repo_name=backend.repo_name,
260 260 commit_id=commit_id,
261 261 comment_id=comment_id),
262 262 params={'csrf_token': self.csrf_token})
263 263
264 264 comments = ChangesetComment.query().all()
265 265 assert len(comments) == 0
266 266
267 267 response = self.app.get(
268 268 route_path('repo_commit',
269 269 repo_name=backend.repo_name, commit_id=commit_id))
270 270 assert_comment_links(response, 0, 0)
271 271
272 272 @pytest.mark.parametrize('renderer, input, output', [
273 273 ('rst', 'plain text', '<p>plain text</p>'),
274 274 ('rst', 'header\n======', '<h1 class="title">header</h1>'),
275 275 ('rst', '*italics*', '<em>italics</em>'),
276 276 ('rst', '**bold**', '<strong>bold</strong>'),
277 277 ('markdown', 'plain text', '<p>plain text</p>'),
278 278 ('markdown', '# header', '<h1>header</h1>'),
279 279 ('markdown', '*italics*', '<em>italics</em>'),
280 280 ('markdown', '**bold**', '<strong>bold</strong>'),
281 281 ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
282 282 'md-header', 'md-italics', 'md-bold', ])
283 283 def test_preview(self, renderer, input, output, backend, xhr_header):
284 284 self.log_user()
285 285 params = {
286 286 'renderer': renderer,
287 287 'text': input,
288 288 'csrf_token': self.csrf_token
289 289 }
290 290 commit_id = '0' * 16 # fake this for tests
291 291 response = self.app.post(
292 292 route_path('repo_commit_comment_preview',
293 293 repo_name=backend.repo_name, commit_id=commit_id,),
294 294 params=params,
295 295 extra_environ=xhr_header)
296 296
297 297 response.mustcontain(output)
298 298
299 299
300 300 def assert_comment_links(response, comments, inline_comments):
301 301 if comments == 1:
302 comments_text = "%d Commit comment" % comments
302 comments_text = "%d General" % comments
303 303 else:
304 comments_text = "%d Commit comments" % comments
304 comments_text = "%d General" % comments
305 305
306 306 if inline_comments == 1:
307 inline_comments_text = "%d Inline Comment" % inline_comments
307 inline_comments_text = "%d Inline" % inline_comments
308 308 else:
309 inline_comments_text = "%d Inline Comments" % inline_comments
309 inline_comments_text = "%d Inline" % inline_comments
310 310
311 311 if comments:
312 312 response.mustcontain('<a href="#comments">%s</a>,' % comments_text)
313 313 else:
314 314 response.mustcontain(comments_text)
315 315
316 316 if inline_comments:
317 317 response.mustcontain(
318 'id="inline-comments-counter">%s</' % inline_comments_text)
318 'id="inline-comments-counter">%s' % inline_comments_text)
319 319 else:
320 320 response.mustcontain(inline_comments_text)
@@ -1,318 +1,327 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 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
23 24 from rhodecode.lib.helpers import _shorten_commit_id
24 25
25 26
26 27 def route_path(name, params=None, **kwargs):
27 28 import urllib
28 29
29 30 base_url = {
30 31 'repo_commit': '/{repo_name}/changeset/{commit_id}',
31 32 'repo_commit_children': '/{repo_name}/changeset_children/{commit_id}',
32 33 'repo_commit_parents': '/{repo_name}/changeset_parents/{commit_id}',
33 34 'repo_commit_raw': '/{repo_name}/changeset-diff/{commit_id}',
34 35 'repo_commit_patch': '/{repo_name}/changeset-patch/{commit_id}',
35 36 'repo_commit_download': '/{repo_name}/changeset-download/{commit_id}',
36 37 'repo_commit_data': '/{repo_name}/changeset-data/{commit_id}',
37 38 'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}',
38 39 }[name].format(**kwargs)
39 40
40 41 if params:
41 42 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
42 43 return base_url
43 44
44 45
45 46 @pytest.mark.usefixtures("app")
46 47 class TestRepoCommitView(object):
47 48
48 49 def test_show_commit(self, backend):
49 50 commit_id = self.commit_id[backend.alias]
50 51 response = self.app.get(route_path(
51 52 'repo_commit', repo_name=backend.repo_name, commit_id=commit_id))
52 53 response.mustcontain('Added a symlink')
53 54 response.mustcontain(commit_id)
54 55 response.mustcontain('No newline at end of file')
55 56
56 57 def test_show_raw(self, backend):
57 58 commit_id = self.commit_id[backend.alias]
58 59 response = self.app.get(route_path(
59 60 'repo_commit_raw',
60 61 repo_name=backend.repo_name, commit_id=commit_id))
61 62 assert response.body == self.diffs[backend.alias]
62 63
63 64 def test_show_raw_patch(self, backend):
64 65 response = self.app.get(route_path(
65 66 'repo_commit_patch', repo_name=backend.repo_name,
66 67 commit_id=self.commit_id[backend.alias]))
67 68 assert response.body == self.patches[backend.alias]
68 69
69 70 def test_commit_download(self, backend):
70 71 response = self.app.get(route_path(
71 72 'repo_commit_download',
72 73 repo_name=backend.repo_name,
73 74 commit_id=self.commit_id[backend.alias]))
74 75 assert response.body == self.diffs[backend.alias]
75 76
76 77 def test_single_commit_page_different_ops(self, backend):
77 78 commit_id = {
78 79 'hg': '603d6c72c46d953420c89d36372f08d9f305f5dd',
79 80 'git': '03fa803d7e9fb14daa9a3089e0d1494eda75d986',
80 81 'svn': '337',
81 82 }
82 83 diff_stat = {
83 'git': '20 files changed: 941 inserted, 286 deleted',
84 'svn': '21 files changed: 943 inserted, 288 deleted',
85 'hg': '21 files changed: 943 inserted, 288 deleted',
84 'hg': (21, 943, 288),
85 'git': (20, 941, 286),
86 'svn': (21, 943, 288),
87 }
86 88
87 }
88 89 commit_id = commit_id[backend.alias]
89 90 response = self.app.get(route_path(
90 91 'repo_commit',
91 92 repo_name=backend.repo_name, commit_id=commit_id))
92 93
93 94 response.mustcontain(_shorten_commit_id(commit_id))
94 response.mustcontain(diff_stat[backend.alias])
95
96 compare_page = ComparePage(response)
97 file_changes = diff_stat[backend.alias]
98 compare_page.contains_change_summary(*file_changes)
95 99
96 100 # files op files
97 101 response.mustcontain('File not present at commit: %s' %
98 102 _shorten_commit_id(commit_id))
99 103
100 104 # svn uses a different filename
101 105 if backend.alias == 'svn':
102 106 response.mustcontain('new file 10644')
103 107 else:
104 108 response.mustcontain('new file 100644')
105 109 response.mustcontain('Changed theme to ADC theme') # commit msg
106 110
107 111 self._check_new_diff_menus(response, right_menu=True)
108 112
109 113 def test_commit_range_page_different_ops(self, backend):
110 114 commit_id_range = {
111 115 'hg': (
112 116 '25d7e49c18b159446cadfa506a5cf8ad1cb04067',
113 117 '603d6c72c46d953420c89d36372f08d9f305f5dd'),
114 118 'git': (
115 119 '6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
116 120 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'),
117 121 'svn': (
118 122 '335',
119 123 '337'),
120 124 }
121 125 commit_ids = commit_id_range[backend.alias]
122 126 commit_id = '%s...%s' % (commit_ids[0], commit_ids[1])
123 127 response = self.app.get(route_path(
124 128 'repo_commit',
125 129 repo_name=backend.repo_name, commit_id=commit_id))
126 130
127 131 response.mustcontain(_shorten_commit_id(commit_ids[0]))
128 132 response.mustcontain(_shorten_commit_id(commit_ids[1]))
129 133
134 compare_page = ComparePage(response)
135
130 136 # svn is special
131 137 if backend.alias == 'svn':
132 138 response.mustcontain('new file 10644')
133 response.mustcontain('1 file changed: 5 inserted, 1 deleted')
134 response.mustcontain('12 files changed: 236 inserted, 22 deleted')
135 response.mustcontain('21 files changed: 943 inserted, 288 deleted')
139 for file_changes in [(1, 5, 1), (12, 236, 22), (21, 943, 288)]:
140 compare_page.contains_change_summary(*file_changes)
136 141 elif backend.alias == 'git':
137 142 response.mustcontain('new file 100644')
138 response.mustcontain('12 files changed: 222 inserted, 20 deleted')
139 response.mustcontain('20 files changed: 941 inserted, 286 deleted')
143 for file_changes in [(12, 222, 20), (20, 941, 286)]:
144 compare_page.contains_change_summary(*file_changes)
140 145 else:
141 146 response.mustcontain('new file 100644')
142 response.mustcontain('12 files changed: 222 inserted, 20 deleted')
143 response.mustcontain('21 files changed: 943 inserted, 288 deleted')
147 for file_changes in [(12, 222, 20), (21, 943, 288)]:
148 compare_page.contains_change_summary(*file_changes)
144 149
145 150 # files op files
146 response.mustcontain('File not present at commit: %s' %
147 _shorten_commit_id(commit_ids[1]))
151 response.mustcontain('File not present at commit: %s' % _shorten_commit_id(commit_ids[1]))
148 152 response.mustcontain('Added docstrings to vcs.cli') # commit msg
149 153 response.mustcontain('Changed theme to ADC theme') # commit msg
150 154
151 155 self._check_new_diff_menus(response)
152 156
153 157 def test_combined_compare_commit_page_different_ops(self, backend):
154 158 commit_id_range = {
155 159 'hg': (
156 160 '4fdd71e9427417b2e904e0464c634fdee85ec5a7',
157 161 '603d6c72c46d953420c89d36372f08d9f305f5dd'),
158 162 'git': (
159 163 'f5fbf9cfd5f1f1be146f6d3b38bcd791a7480c13',
160 164 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'),
161 165 'svn': (
162 166 '335',
163 167 '337'),
164 168 }
165 169 commit_ids = commit_id_range[backend.alias]
166 170 response = self.app.get(route_path(
167 171 'repo_compare',
168 172 repo_name=backend.repo_name,
169 173 source_ref_type='rev', source_ref=commit_ids[0],
170 174 target_ref_type='rev', target_ref=commit_ids[1], ))
171 175
172 176 response.mustcontain(_shorten_commit_id(commit_ids[0]))
173 177 response.mustcontain(_shorten_commit_id(commit_ids[1]))
174 178
175 179 # files op files
176 180 response.mustcontain('File not present at commit: %s' %
177 181 _shorten_commit_id(commit_ids[1]))
178 182
183 compare_page = ComparePage(response)
184
179 185 # svn is special
180 186 if backend.alias == 'svn':
181 187 response.mustcontain('new file 10644')
182 response.mustcontain('32 files changed: 1179 inserted, 310 deleted')
188 file_changes = (32, 1179, 310)
189 compare_page.contains_change_summary(*file_changes)
183 190 elif backend.alias == 'git':
184 191 response.mustcontain('new file 100644')
185 response.mustcontain('31 files changed: 1163 inserted, 306 deleted')
192 file_changes = (31, 1163, 306)
193 compare_page.contains_change_summary(*file_changes)
186 194 else:
187 195 response.mustcontain('new file 100644')
188 response.mustcontain('32 files changed: 1165 inserted, 308 deleted')
196 file_changes = (32, 1165, 308)
197 compare_page.contains_change_summary(*file_changes)
189 198
190 199 response.mustcontain('Added docstrings to vcs.cli') # commit msg
191 200 response.mustcontain('Changed theme to ADC theme') # commit msg
192 201
193 202 self._check_new_diff_menus(response)
194 203
195 204 def test_changeset_range(self, backend):
196 205 self._check_changeset_range(
197 206 backend, self.commit_id_range, self.commit_id_range_result)
198 207
199 208 def test_changeset_range_with_initial_commit(self, backend):
200 209 commit_id_range = {
201 210 'hg': (
202 211 'b986218ba1c9b0d6a259fac9b050b1724ed8e545'
203 212 '...6cba7170863a2411822803fa77a0a264f1310b35'),
204 213 'git': (
205 214 'c1214f7e79e02fc37156ff215cd71275450cffc3'
206 215 '...fa6600f6848800641328adbf7811fd2372c02ab2'),
207 216 'svn': '1...3',
208 217 }
209 218 commit_id_range_result = {
210 219 'hg': ['b986218ba1c9', '3d8f361e72ab', '6cba7170863a'],
211 220 'git': ['c1214f7e79e0', '38b5fe81f109', 'fa6600f68488'],
212 221 'svn': ['1', '2', '3'],
213 222 }
214 223 self._check_changeset_range(
215 224 backend, commit_id_range, commit_id_range_result)
216 225
217 226 def _check_changeset_range(
218 227 self, backend, commit_id_ranges, commit_id_range_result):
219 228 response = self.app.get(
220 229 route_path('repo_commit',
221 230 repo_name=backend.repo_name,
222 231 commit_id=commit_id_ranges[backend.alias]))
223 232
224 233 expected_result = commit_id_range_result[backend.alias]
225 234 response.mustcontain('{} commits'.format(len(expected_result)))
226 235 for commit_id in expected_result:
227 236 response.mustcontain(commit_id)
228 237
229 238 commit_id = {
230 239 'hg': '2062ec7beeeaf9f44a1c25c41479565040b930b2',
231 240 'svn': '393',
232 241 'git': 'fd627b9e0dd80b47be81af07c4a98518244ed2f7',
233 242 }
234 243
235 244 commit_id_range = {
236 245 'hg': (
237 246 'a53d9201d4bc278910d416d94941b7ea007ecd52'
238 247 '...2062ec7beeeaf9f44a1c25c41479565040b930b2'),
239 248 'git': (
240 249 '7ab37bc680b4aa72c34d07b230c866c28e9fc204'
241 250 '...fd627b9e0dd80b47be81af07c4a98518244ed2f7'),
242 251 'svn': '391...393',
243 252 }
244 253
245 254 commit_id_range_result = {
246 255 'hg': ['a53d9201d4bc', '96507bd11ecc', '2062ec7beeea'],
247 256 'git': ['7ab37bc680b4', '5f2c6ee19592', 'fd627b9e0dd8'],
248 257 'svn': ['391', '392', '393'],
249 258 }
250 259
251 260 diffs = {
252 261 'hg': r"""diff --git a/README b/README
253 262 new file mode 120000
254 263 --- /dev/null
255 264 +++ b/README
256 265 @@ -0,0 +1,1 @@
257 266 +README.rst
258 267 \ No newline at end of file
259 268 """,
260 269 'git': r"""diff --git a/README b/README
261 270 new file mode 120000
262 271 index 0000000..92cacd2
263 272 --- /dev/null
264 273 +++ b/README
265 274 @@ -0,0 +1 @@
266 275 +README.rst
267 276 \ No newline at end of file
268 277 """,
269 278 'svn': """Index: README
270 279 ===================================================================
271 280 diff --git a/README b/README
272 281 new file mode 10644
273 282 --- /dev/null\t(revision 0)
274 283 +++ b/README\t(revision 393)
275 284 @@ -0,0 +1 @@
276 285 +link README.rst
277 286 \\ No newline at end of file
278 287 """,
279 288 }
280 289
281 290 patches = {
282 291 'hg': r"""# HG changeset patch
283 292 # User Marcin Kuzminski <marcin@python-works.com>
284 293 # Date 2014-01-07 12:21:40
285 294 # Node ID 2062ec7beeeaf9f44a1c25c41479565040b930b2
286 295 # Parent 96507bd11ecc815ebc6270fdf6db110928c09c1e
287 296
288 297 Added a symlink
289 298
290 299 """ + diffs['hg'],
291 300 'git': r"""From fd627b9e0dd80b47be81af07c4a98518244ed2f7 2014-01-07 12:22:20
292 301 From: Marcin Kuzminski <marcin@python-works.com>
293 302 Date: 2014-01-07 12:22:20
294 303 Subject: [PATCH] Added a symlink
295 304
296 305 ---
297 306
298 307 """ + diffs['git'],
299 308 'svn': r"""# SVN changeset patch
300 309 # User marcin
301 310 # Date 2014-09-02 12:25:22.071142
302 311 # Revision 393
303 312
304 313 Added a symlink
305 314
306 315 """ + diffs['svn'],
307 316 }
308 317
309 318 def _check_new_diff_menus(self, response, right_menu=False,):
310 319 # individual file diff menus
311 320 for elem in ['Show file before', 'Show file after']:
312 321 response.mustcontain(elem)
313 322
314 323 # right pane diff menus
315 324 if right_menu:
316 for elem in ['Hide whitespace changes', 'Toggle Wide Mode diff',
325 for elem in ['Hide whitespace changes', 'Toggle wide diff',
317 326 'Show full context diff']:
318 327 response.mustcontain(elem)
@@ -1,666 +1,666 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 mock
22 22 import pytest
23 23 import lxml.html
24 24
25 25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
26 26 from rhodecode.tests import assert_session_flash
27 27 from rhodecode.tests.utils import AssertResponse, commit_change
28 28
29 29
30 30 def route_path(name, params=None, **kwargs):
31 31 import urllib
32 32
33 33 base_url = {
34 34 'repo_compare_select': '/{repo_name}/compare',
35 35 'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}',
36 36 }[name].format(**kwargs)
37 37
38 38 if params:
39 39 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
40 40 return base_url
41 41
42 42
43 43 @pytest.mark.usefixtures("autologin_user", "app")
44 44 class TestCompareView(object):
45 45
46 46 def test_compare_index_is_reached_at_least_once(self, backend):
47 47 repo = backend.repo
48 48 self.app.get(
49 49 route_path('repo_compare_select', repo_name=repo.repo_name))
50 50
51 51 @pytest.mark.xfail_backends("svn", reason="Requires pull")
52 52 def test_compare_remote_with_different_commit_indexes(self, backend):
53 53 # Preparing the following repository structure:
54 54 #
55 55 # Origin repository has two commits:
56 56 #
57 57 # 0 1
58 58 # A -- D
59 59 #
60 60 # The fork of it has a few more commits and "D" has a commit index
61 61 # which does not exist in origin.
62 62 #
63 63 # 0 1 2 3 4
64 64 # A -- -- -- D -- E
65 65 # \- B -- C
66 66 #
67 67
68 68 fork = backend.create_repo()
69 69
70 70 # prepare fork
71 71 commit0 = commit_change(
72 72 fork.repo_name, filename='file1', content='A',
73 73 message='A', vcs_type=backend.alias, parent=None, newfile=True)
74 74
75 75 commit1 = commit_change(
76 76 fork.repo_name, filename='file1', content='B',
77 77 message='B, child of A', vcs_type=backend.alias, parent=commit0)
78 78
79 79 commit_change( # commit 2
80 80 fork.repo_name, filename='file1', content='C',
81 81 message='C, child of B', vcs_type=backend.alias, parent=commit1)
82 82
83 83 commit3 = commit_change(
84 84 fork.repo_name, filename='file1', content='D',
85 85 message='D, child of A', vcs_type=backend.alias, parent=commit0)
86 86
87 87 commit4 = commit_change(
88 88 fork.repo_name, filename='file1', content='E',
89 89 message='E, child of D', vcs_type=backend.alias, parent=commit3)
90 90
91 91 # prepare origin repository, taking just the history up to D
92 92 origin = backend.create_repo()
93 93
94 94 origin_repo = origin.scm_instance(cache=False)
95 95 origin_repo.config.clear_section('hooks')
96 96 origin_repo.pull(fork.repo_full_path, commit_ids=[commit3.raw_id])
97 97 origin_repo = origin.scm_instance(cache=False) # cache rebuild
98 98
99 99 # Verify test fixture setup
100 100 # This does not work for git
101 101 if backend.alias != 'git':
102 102 assert 5 == len(fork.scm_instance().commit_ids)
103 103 assert 2 == len(origin_repo.commit_ids)
104 104
105 105 # Comparing the revisions
106 106 response = self.app.get(
107 107 route_path('repo_compare',
108 108 repo_name=origin.repo_name,
109 109 source_ref_type="rev", source_ref=commit3.raw_id,
110 110 target_ref_type="rev", target_ref=commit4.raw_id,
111 111 params=dict(merge='1', target_repo=fork.repo_name)
112 112 ))
113 113
114 114 compare_page = ComparePage(response)
115 115 compare_page.contains_commits([commit4])
116 116
117 117 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
118 118 def test_compare_forks_on_branch_extra_commits(self, backend):
119 119 repo1 = backend.create_repo()
120 120
121 121 # commit something !
122 122 commit0 = commit_change(
123 123 repo1.repo_name, filename='file1', content='line1\n',
124 124 message='commit1', vcs_type=backend.alias, parent=None,
125 125 newfile=True)
126 126
127 127 # fork this repo
128 128 repo2 = backend.create_fork()
129 129
130 130 # add two extra commit into fork
131 131 commit1 = commit_change(
132 132 repo2.repo_name, filename='file1', content='line1\nline2\n',
133 133 message='commit2', vcs_type=backend.alias, parent=commit0)
134 134
135 135 commit2 = commit_change(
136 136 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
137 137 message='commit3', vcs_type=backend.alias, parent=commit1)
138 138
139 139 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
140 140 commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME
141 141
142 142 response = self.app.get(
143 143 route_path('repo_compare',
144 144 repo_name=repo1.repo_name,
145 145 source_ref_type="branch", source_ref=commit_id2,
146 146 target_ref_type="branch", target_ref=commit_id1,
147 147 params=dict(merge='1', target_repo=repo2.repo_name)
148 148 ))
149 149
150 150 response.mustcontain('%s@%s' % (repo1.repo_name, commit_id2))
151 151 response.mustcontain('%s@%s' % (repo2.repo_name, commit_id1))
152 152
153 153 compare_page = ComparePage(response)
154 154 compare_page.contains_change_summary(1, 2, 0)
155 155 compare_page.contains_commits([commit1, commit2])
156 156
157 157 anchor = 'a_c-{}-826e8142e6ba'.format(commit0.short_id)
158 158 compare_page.contains_file_links_and_anchors([('file1', anchor), ])
159 159
160 160 # Swap is removed when comparing branches since it's a PR feature and
161 161 # it is then a preview mode
162 162 compare_page.swap_is_hidden()
163 163 compare_page.target_source_are_disabled()
164 164
165 165 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
166 166 def test_compare_forks_on_branch_extra_commits_origin_has_incomming(self, backend):
167 167 repo1 = backend.create_repo()
168 168
169 169 # commit something !
170 170 commit0 = commit_change(
171 171 repo1.repo_name, filename='file1', content='line1\n',
172 172 message='commit1', vcs_type=backend.alias, parent=None,
173 173 newfile=True)
174 174
175 175 # fork this repo
176 176 repo2 = backend.create_fork()
177 177
178 178 # now commit something to origin repo
179 179 commit_change(
180 180 repo1.repo_name, filename='file2', content='line1file2\n',
181 181 message='commit2', vcs_type=backend.alias, parent=commit0,
182 182 newfile=True)
183 183
184 184 # add two extra commit into fork
185 185 commit1 = commit_change(
186 186 repo2.repo_name, filename='file1', content='line1\nline2\n',
187 187 message='commit2', vcs_type=backend.alias, parent=commit0)
188 188
189 189 commit2 = commit_change(
190 190 repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
191 191 message='commit3', vcs_type=backend.alias, parent=commit1)
192 192
193 193 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
194 194 commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME
195 195
196 196 response = self.app.get(
197 197 route_path('repo_compare',
198 198 repo_name=repo1.repo_name,
199 199 source_ref_type="branch", source_ref=commit_id2,
200 200 target_ref_type="branch", target_ref=commit_id1,
201 201 params=dict(merge='1', target_repo=repo2.repo_name),
202 202 ))
203 203
204 204 response.mustcontain('%s@%s' % (repo1.repo_name, commit_id2))
205 205 response.mustcontain('%s@%s' % (repo2.repo_name, commit_id1))
206 206
207 207 compare_page = ComparePage(response)
208 208 compare_page.contains_change_summary(1, 2, 0)
209 209 compare_page.contains_commits([commit1, commit2])
210 210 anchor = 'a_c-{}-826e8142e6ba'.format(commit0.short_id)
211 211 compare_page.contains_file_links_and_anchors([('file1', anchor), ])
212 212
213 213 # Swap is removed when comparing branches since it's a PR feature and
214 214 # it is then a preview mode
215 215 compare_page.swap_is_hidden()
216 216 compare_page.target_source_are_disabled()
217 217
218 218 @pytest.mark.xfail_backends("svn")
219 219 # TODO(marcink): no svn support for compare two seperate repos
220 220 def test_compare_of_unrelated_forks(self, backend):
221 221 orig = backend.create_repo(number_of_commits=1)
222 222 fork = backend.create_repo(number_of_commits=1)
223 223
224 224 response = self.app.get(
225 225 route_path('repo_compare',
226 226 repo_name=orig.repo_name,
227 227 source_ref_type="rev", source_ref="tip",
228 228 target_ref_type="rev", target_ref="tip",
229 229 params=dict(merge='1', target_repo=fork.repo_name),
230 230 ),
231 231 status=302)
232 232 response = response.follow()
233 233 response.mustcontain("Repositories unrelated.")
234 234
235 235 @pytest.mark.xfail_backends("svn")
236 236 def test_compare_cherry_pick_commits_from_bottom(self, backend):
237 237
238 238 # repo1:
239 239 # commit0:
240 240 # commit1:
241 241 # repo1-fork- in which we will cherry pick bottom commits
242 242 # commit0:
243 243 # commit1:
244 244 # commit2: x
245 245 # commit3: x
246 246 # commit4: x
247 247 # commit5:
248 248 # make repo1, and commit1+commit2
249 249
250 250 repo1 = backend.create_repo()
251 251
252 252 # commit something !
253 253 commit0 = commit_change(
254 254 repo1.repo_name, filename='file1', content='line1\n',
255 255 message='commit1', vcs_type=backend.alias, parent=None,
256 256 newfile=True)
257 257 commit1 = commit_change(
258 258 repo1.repo_name, filename='file1', content='line1\nline2\n',
259 259 message='commit2', vcs_type=backend.alias, parent=commit0)
260 260
261 261 # fork this repo
262 262 repo2 = backend.create_fork()
263 263
264 264 # now make commit3-6
265 265 commit2 = commit_change(
266 266 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
267 267 message='commit3', vcs_type=backend.alias, parent=commit1)
268 268 commit3 = commit_change(
269 269 repo1.repo_name, filename='file1',
270 270 content='line1\nline2\nline3\nline4\n', message='commit4',
271 271 vcs_type=backend.alias, parent=commit2)
272 272 commit4 = commit_change(
273 273 repo1.repo_name, filename='file1',
274 274 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
275 275 vcs_type=backend.alias, parent=commit3)
276 276 commit_change( # commit 5
277 277 repo1.repo_name, filename='file1',
278 278 content='line1\nline2\nline3\nline4\nline5\nline6\n',
279 279 message='commit6', vcs_type=backend.alias, parent=commit4)
280 280
281 281 response = self.app.get(
282 282 route_path('repo_compare',
283 283 repo_name=repo2.repo_name,
284 284 # parent of commit2, in target repo2
285 285 source_ref_type="rev", source_ref=commit1.raw_id,
286 286 target_ref_type="rev", target_ref=commit4.raw_id,
287 287 params=dict(merge='1', target_repo=repo1.repo_name),
288 288 ))
289 289 response.mustcontain('%s@%s' % (repo2.repo_name, commit1.short_id))
290 290 response.mustcontain('%s@%s' % (repo1.repo_name, commit4.short_id))
291 291
292 292 # files
293 293 compare_page = ComparePage(response)
294 294 compare_page.contains_change_summary(1, 3, 0)
295 295 compare_page.contains_commits([commit2, commit3, commit4])
296 296 anchor = 'a_c-{}-826e8142e6ba'.format(commit1.short_id)
297 297 compare_page.contains_file_links_and_anchors([('file1', anchor),])
298 298
299 299 @pytest.mark.xfail_backends("svn")
300 300 def test_compare_cherry_pick_commits_from_top(self, backend):
301 301 # repo1:
302 302 # commit0:
303 303 # commit1:
304 304 # repo1-fork- in which we will cherry pick bottom commits
305 305 # commit0:
306 306 # commit1:
307 307 # commit2:
308 308 # commit3: x
309 309 # commit4: x
310 310 # commit5: x
311 311
312 312 # make repo1, and commit1+commit2
313 313 repo1 = backend.create_repo()
314 314
315 315 # commit something !
316 316 commit0 = commit_change(
317 317 repo1.repo_name, filename='file1', content='line1\n',
318 318 message='commit1', vcs_type=backend.alias, parent=None,
319 319 newfile=True)
320 320 commit1 = commit_change(
321 321 repo1.repo_name, filename='file1', content='line1\nline2\n',
322 322 message='commit2', vcs_type=backend.alias, parent=commit0)
323 323
324 324 # fork this repo
325 325 backend.create_fork()
326 326
327 327 # now make commit3-6
328 328 commit2 = commit_change(
329 329 repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
330 330 message='commit3', vcs_type=backend.alias, parent=commit1)
331 331 commit3 = commit_change(
332 332 repo1.repo_name, filename='file1',
333 333 content='line1\nline2\nline3\nline4\n', message='commit4',
334 334 vcs_type=backend.alias, parent=commit2)
335 335 commit4 = commit_change(
336 336 repo1.repo_name, filename='file1',
337 337 content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
338 338 vcs_type=backend.alias, parent=commit3)
339 339 commit5 = commit_change(
340 340 repo1.repo_name, filename='file1',
341 341 content='line1\nline2\nline3\nline4\nline5\nline6\n',
342 342 message='commit6', vcs_type=backend.alias, parent=commit4)
343 343
344 344 response = self.app.get(
345 345 route_path('repo_compare',
346 346 repo_name=repo1.repo_name,
347 347 # parent of commit3, not in source repo2
348 348 source_ref_type="rev", source_ref=commit2.raw_id,
349 349 target_ref_type="rev", target_ref=commit5.raw_id,
350 350 params=dict(merge='1'),))
351 351
352 352 response.mustcontain('%s@%s' % (repo1.repo_name, commit2.short_id))
353 353 response.mustcontain('%s@%s' % (repo1.repo_name, commit5.short_id))
354 354
355 355 compare_page = ComparePage(response)
356 356 compare_page.contains_change_summary(1, 3, 0)
357 357 compare_page.contains_commits([commit3, commit4, commit5])
358 358
359 359 # files
360 360 anchor = 'a_c-{}-826e8142e6ba'.format(commit2.short_id)
361 361 compare_page.contains_file_links_and_anchors([('file1', anchor),])
362 362
363 363 @pytest.mark.xfail_backends("svn")
364 364 def test_compare_remote_branches(self, backend):
365 365 repo1 = backend.repo
366 366 repo2 = backend.create_fork()
367 367
368 368 commit_id1 = repo1.get_commit(commit_idx=3).raw_id
369 369 commit_id1_short = repo1.get_commit(commit_idx=3).short_id
370 370 commit_id2 = repo1.get_commit(commit_idx=6).raw_id
371 371 commit_id2_short = repo1.get_commit(commit_idx=6).short_id
372 372
373 373 response = self.app.get(
374 374 route_path('repo_compare',
375 375 repo_name=repo1.repo_name,
376 376 source_ref_type="rev", source_ref=commit_id1,
377 377 target_ref_type="rev", target_ref=commit_id2,
378 378 params=dict(merge='1', target_repo=repo2.repo_name),
379 379 ))
380 380
381 381 response.mustcontain('%s@%s' % (repo1.repo_name, commit_id1))
382 382 response.mustcontain('%s@%s' % (repo2.repo_name, commit_id2))
383 383
384 384 compare_page = ComparePage(response)
385 385
386 386 # outgoing commits between those commits
387 387 compare_page.contains_commits(
388 388 [repo2.get_commit(commit_idx=x) for x in [4, 5, 6]])
389 389
390 390 # files
391 391 compare_page.contains_file_links_and_anchors([
392 392 ('vcs/backends/hg.py', 'a_c-{}-9c390eb52cd6'.format(commit_id2_short)),
393 393 ('vcs/backends/__init__.py', 'a_c-{}-41b41c1f2796'.format(commit_id1_short)),
394 394 ('vcs/backends/base.py', 'a_c-{}-2f574d260608'.format(commit_id1_short)),
395 395 ])
396 396
397 397 @pytest.mark.xfail_backends("svn")
398 398 def test_source_repo_new_commits_after_forking_simple_diff(self, backend):
399 399 repo1 = backend.create_repo()
400 400 r1_name = repo1.repo_name
401 401
402 402 commit0 = commit_change(
403 403 repo=r1_name, filename='file1',
404 404 content='line1', message='commit1', vcs_type=backend.alias,
405 405 newfile=True)
406 406 assert repo1.scm_instance().commit_ids == [commit0.raw_id]
407 407
408 408 # fork the repo1
409 409 repo2 = backend.create_fork()
410 410 assert repo2.scm_instance().commit_ids == [commit0.raw_id]
411 411
412 412 self.r2_id = repo2.repo_id
413 413 r2_name = repo2.repo_name
414 414
415 415 commit1 = commit_change(
416 416 repo=r2_name, filename='file1-fork',
417 417 content='file1-line1-from-fork', message='commit1-fork',
418 418 vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
419 419 newfile=True)
420 420
421 421 commit2 = commit_change(
422 422 repo=r2_name, filename='file2-fork',
423 423 content='file2-line1-from-fork', message='commit2-fork',
424 424 vcs_type=backend.alias, parent=commit1,
425 425 newfile=True)
426 426
427 427 commit_change( # commit 3
428 428 repo=r2_name, filename='file3-fork',
429 429 content='file3-line1-from-fork', message='commit3-fork',
430 430 vcs_type=backend.alias, parent=commit2, newfile=True)
431 431
432 432 # compare !
433 433 commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
434 434 commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME
435 435
436 436 response = self.app.get(
437 437 route_path('repo_compare',
438 438 repo_name=r2_name,
439 439 source_ref_type="branch", source_ref=commit_id1,
440 440 target_ref_type="branch", target_ref=commit_id2,
441 441 params=dict(merge='1', target_repo=r1_name),
442 442 ))
443 443
444 444 response.mustcontain('%s@%s' % (r2_name, commit_id1))
445 445 response.mustcontain('%s@%s' % (r1_name, commit_id2))
446 446 response.mustcontain('No files')
447 447 response.mustcontain('No commits in this compare')
448 448
449 449 commit0 = commit_change(
450 450 repo=r1_name, filename='file2',
451 451 content='line1-added-after-fork', message='commit2-parent',
452 452 vcs_type=backend.alias, parent=None, newfile=True)
453 453
454 454 # compare !
455 455 response = self.app.get(
456 456 route_path('repo_compare',
457 457 repo_name=r2_name,
458 458 source_ref_type="branch", source_ref=commit_id1,
459 459 target_ref_type="branch", target_ref=commit_id2,
460 460 params=dict(merge='1', target_repo=r1_name),
461 461 ))
462 462
463 463 response.mustcontain('%s@%s' % (r2_name, commit_id1))
464 464 response.mustcontain('%s@%s' % (r1_name, commit_id2))
465 465
466 466 response.mustcontain("""commit2-parent""")
467 467 response.mustcontain("""line1-added-after-fork""")
468 468 compare_page = ComparePage(response)
469 469 compare_page.contains_change_summary(1, 1, 0)
470 470
471 471 @pytest.mark.xfail_backends("svn")
472 472 def test_compare_commits(self, backend, xhr_header):
473 473 commit0 = backend.repo.get_commit(commit_idx=0)
474 474 commit1 = backend.repo.get_commit(commit_idx=1)
475 475
476 476 response = self.app.get(
477 477 route_path('repo_compare',
478 478 repo_name=backend.repo_name,
479 479 source_ref_type="rev", source_ref=commit0.raw_id,
480 480 target_ref_type="rev", target_ref=commit1.raw_id,
481 481 params=dict(merge='1')
482 482 ),
483 483 extra_environ=xhr_header, )
484 484
485 485 # outgoing commits between those commits
486 486 compare_page = ComparePage(response)
487 487 compare_page.contains_commits(commits=[commit1], ancestors=[commit0])
488 488
489 489 def test_errors_when_comparing_unknown_source_repo(self, backend):
490 490 repo = backend.repo
491 491 badrepo = 'badrepo'
492 492
493 493 response = self.app.get(
494 494 route_path('repo_compare',
495 495 repo_name=badrepo,
496 496 source_ref_type="rev", source_ref='tip',
497 497 target_ref_type="rev", target_ref='tip',
498 498 params=dict(merge='1', target_repo=repo.repo_name)
499 499 ),
500 500 status=404)
501 501
502 502 def test_errors_when_comparing_unknown_target_repo(self, backend):
503 503 repo = backend.repo
504 504 badrepo = 'badrepo'
505 505
506 506 response = self.app.get(
507 507 route_path('repo_compare',
508 508 repo_name=repo.repo_name,
509 509 source_ref_type="rev", source_ref='tip',
510 510 target_ref_type="rev", target_ref='tip',
511 511 params=dict(merge='1', target_repo=badrepo),
512 512 ),
513 513 status=302)
514 514 redirected = response.follow()
515 515 redirected.mustcontain(
516 516 'Could not find the target repo: `{}`'.format(badrepo))
517 517
518 518 def test_compare_not_in_preview_mode(self, backend_stub):
519 519 commit0 = backend_stub.repo.get_commit(commit_idx=0)
520 520 commit1 = backend_stub.repo.get_commit(commit_idx=1)
521 521
522 522 response = self.app.get(
523 523 route_path('repo_compare',
524 524 repo_name=backend_stub.repo_name,
525 525 source_ref_type="rev", source_ref=commit0.raw_id,
526 526 target_ref_type="rev", target_ref=commit1.raw_id,
527 527 ))
528 528
529 529 # outgoing commits between those commits
530 530 compare_page = ComparePage(response)
531 531 compare_page.swap_is_visible()
532 532 compare_page.target_source_are_enabled()
533 533
534 534 def test_compare_of_fork_with_largefiles(self, backend_hg, settings_util):
535 535 orig = backend_hg.create_repo(number_of_commits=1)
536 536 fork = backend_hg.create_fork()
537 537
538 538 settings_util.create_repo_rhodecode_ui(
539 539 orig, 'extensions', value='', key='largefiles', active=False)
540 540 settings_util.create_repo_rhodecode_ui(
541 541 fork, 'extensions', value='', key='largefiles', active=True)
542 542
543 543 compare_module = ('rhodecode.lib.vcs.backends.hg.repository.'
544 544 'MercurialRepository.compare')
545 545 with mock.patch(compare_module) as compare_mock:
546 546 compare_mock.side_effect = RepositoryRequirementError()
547 547
548 548 response = self.app.get(
549 549 route_path('repo_compare',
550 550 repo_name=orig.repo_name,
551 551 source_ref_type="rev", source_ref="tip",
552 552 target_ref_type="rev", target_ref="tip",
553 553 params=dict(merge='1', target_repo=fork.repo_name),
554 554 ),
555 555 status=302)
556 556
557 557 assert_session_flash(
558 558 response,
559 559 'Could not compare repos with different large file settings')
560 560
561 561
562 562 @pytest.mark.usefixtures("autologin_user")
563 563 class TestCompareControllerSvn(object):
564 564
565 565 def test_supports_references_with_path(self, app, backend_svn):
566 566 repo = backend_svn['svn-simple-layout']
567 567 commit_id = repo.get_commit(commit_idx=-1).raw_id
568 568 response = app.get(
569 569 route_path('repo_compare',
570 570 repo_name=repo.repo_name,
571 571 source_ref_type="tag",
572 572 source_ref="%s@%s" % ('tags/v0.1', commit_id),
573 573 target_ref_type="tag",
574 574 target_ref="%s@%s" % ('tags/v0.2', commit_id),
575 575 params=dict(merge='1'),
576 576 ),
577 577 status=200)
578 578
579 579 # Expecting no commits, since both paths are at the same revision
580 580 response.mustcontain('No commits in this compare')
581 581
582 582 # Should find only one file changed when comparing those two tags
583 583 response.mustcontain('example.py')
584 584 compare_page = ComparePage(response)
585 585 compare_page.contains_change_summary(1, 5, 1)
586 586
587 587 def test_shows_commits_if_different_ids(self, app, backend_svn):
588 588 repo = backend_svn['svn-simple-layout']
589 589 source_id = repo.get_commit(commit_idx=-6).raw_id
590 590 target_id = repo.get_commit(commit_idx=-1).raw_id
591 591 response = app.get(
592 592 route_path('repo_compare',
593 593 repo_name=repo.repo_name,
594 594 source_ref_type="tag",
595 595 source_ref="%s@%s" % ('tags/v0.1', source_id),
596 596 target_ref_type="tag",
597 597 target_ref="%s@%s" % ('tags/v0.2', target_id),
598 598 params=dict(merge='1')
599 599 ),
600 600 status=200)
601 601
602 602 # It should show commits
603 603 assert 'No commits in this compare' not in response.body
604 604
605 605 # Should find only one file changed when comparing those two tags
606 606 response.mustcontain('example.py')
607 607 compare_page = ComparePage(response)
608 608 compare_page.contains_change_summary(1, 5, 1)
609 609
610 610
611 611 class ComparePage(AssertResponse):
612 612 """
613 613 Abstracts the page template from the tests
614 614 """
615 615
616 616 def contains_file_links_and_anchors(self, files):
617 617 doc = lxml.html.fromstring(self.response.body)
618 618 for filename, file_id in files:
619 619 self.contains_one_anchor(file_id)
620 620 diffblock = doc.cssselect('[data-f-path="%s"]' % filename)
621 621 assert len(diffblock) == 2
622 622 assert len(diffblock[0].cssselect('a[href="#%s"]' % file_id)) == 1
623 623
624 624 def contains_change_summary(self, files_changed, inserted, deleted):
625 625 template = (
626 "{files_changed} file{plural} changed: "
627 "{inserted} inserted, {deleted} deleted")
626 '{files_changed} file{plural} changed: '
627 '<span class="op-added">{inserted} inserted</span>, <span class="op-deleted">{deleted} deleted</span>')
628 628 self.response.mustcontain(template.format(
629 629 files_changed=files_changed,
630 630 plural="s" if files_changed > 1 else "",
631 631 inserted=inserted,
632 632 deleted=deleted))
633 633
634 634 def contains_commits(self, commits, ancestors=None):
635 635 response = self.response
636 636
637 637 for commit in commits:
638 638 # Expecting to see the commit message in an element which
639 639 # has the ID "c-{commit.raw_id}"
640 640 self.element_contains('#c-' + commit.raw_id, commit.message)
641 641 self.contains_one_link(
642 642 'r%s:%s' % (commit.idx, commit.short_id),
643 643 self._commit_url(commit))
644 644 if ancestors:
645 645 response.mustcontain('Ancestor')
646 646 for ancestor in ancestors:
647 647 self.contains_one_link(
648 648 ancestor.short_id, self._commit_url(ancestor))
649 649
650 650 def _commit_url(self, commit):
651 651 return '/%s/changeset/%s' % (commit.repository.name, commit.raw_id)
652 652
653 653 def swap_is_hidden(self):
654 654 assert '<a id="btn-swap"' not in self.response.text
655 655
656 656 def swap_is_visible(self):
657 657 assert '<a id="btn-swap"' in self.response.text
658 658
659 659 def target_source_are_disabled(self):
660 660 response = self.response
661 661 response.mustcontain("var enable_fields = false;")
662 662 response.mustcontain('.select2("enable", enable_fields)')
663 663
664 664 def target_source_are_enabled(self):
665 665 response = self.response
666 666 response.mustcontain("var enable_fields = true;")
@@ -1,259 +1,265 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 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
23 24 from rhodecode.lib.vcs import nodes
24 25 from rhodecode.lib.vcs.backends.base import EmptyCommit
25 26 from rhodecode.tests.fixture import Fixture
26 27 from rhodecode.tests.utils import commit_change
27 28
28 29 fixture = Fixture()
29 30
30 31
31 32 def route_path(name, params=None, **kwargs):
32 33 import urllib
33 34
34 35 base_url = {
35 36 'repo_compare_select': '/{repo_name}/compare',
36 37 'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}',
37 38 }[name].format(**kwargs)
38 39
39 40 if params:
40 41 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
41 42 return base_url
42 43
43 44
44 45 @pytest.mark.usefixtures("autologin_user", "app")
45 46 class TestSideBySideDiff(object):
46 47
47 48 def test_diff_sidebyside_single_commit(self, app, backend):
48 49 commit_id_range = {
49 50 'hg': {
50 51 'commits': ['25d7e49c18b159446cadfa506a5cf8ad1cb04067',
51 52 '603d6c72c46d953420c89d36372f08d9f305f5dd'],
52 'changes': '21 files changed: 943 inserted, 288 deleted'
53 'changes': (21, 943, 288),
53 54 },
54 55 'git': {
55 56 'commits': ['6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
56 57 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
57 'changes': '20 files changed: 941 inserted, 286 deleted'
58 'changes': (20, 941, 286),
58 59 },
59 60
60 61 'svn': {
61 62 'commits': ['336',
62 63 '337'],
63 'changes': '21 files changed: 943 inserted, 288 deleted'
64 'changes': (21, 943, 288),
64 65 },
65 66 }
66 67
67 68 commit_info = commit_id_range[backend.alias]
68 69 commit2, commit1 = commit_info['commits']
69 70 file_changes = commit_info['changes']
70 71
71 72 response = self.app.get(route_path(
72 73 'repo_compare',
73 74 repo_name=backend.repo_name,
74 75 source_ref_type='rev',
75 76 source_ref=commit2,
76 77 target_repo=backend.repo_name,
77 78 target_ref_type='rev',
78 79 target_ref=commit1,
79 80 params=dict(target_repo=backend.repo_name, diffmode='sidebyside')
80 81 ))
81 82
82 response.mustcontain(file_changes)
83 compare_page = ComparePage(response)
84 compare_page.contains_change_summary(*file_changes)
83 85 response.mustcontain('Expand 1 commit')
84 86
85 87 def test_diff_sidebyside_two_commits(self, app, backend):
86 88 commit_id_range = {
87 89 'hg': {
88 90 'commits': ['4fdd71e9427417b2e904e0464c634fdee85ec5a7',
89 91 '603d6c72c46d953420c89d36372f08d9f305f5dd'],
90 'changes': '32 files changed: 1165 inserted, 308 deleted'
92 'changes': (32, 1165, 308),
91 93 },
92 94 'git': {
93 95 'commits': ['f5fbf9cfd5f1f1be146f6d3b38bcd791a7480c13',
94 96 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
95 'changes': '31 files changed: 1163 inserted, 306 deleted'
97 'changes': (31, 1163, 306),
96 98 },
97 99
98 100 'svn': {
99 101 'commits': ['335',
100 102 '337'],
101 'changes': '32 files changed: 1179 inserted, 310 deleted'
103 'changes': (32, 1179, 310),
102 104 },
103 105 }
104 106
105 107 commit_info = commit_id_range[backend.alias]
106 108 commit2, commit1 = commit_info['commits']
107 109 file_changes = commit_info['changes']
108 110
109 111 response = self.app.get(route_path(
110 112 'repo_compare',
111 113 repo_name=backend.repo_name,
112 114 source_ref_type='rev',
113 115 source_ref=commit2,
114 116 target_repo=backend.repo_name,
115 117 target_ref_type='rev',
116 118 target_ref=commit1,
117 119 params=dict(target_repo=backend.repo_name, diffmode='sidebyside')
118 120 ))
119 121
120 response.mustcontain(file_changes)
122 compare_page = ComparePage(response)
123 compare_page.contains_change_summary(*file_changes)
124
121 125 response.mustcontain('Expand 2 commits')
122 126
123 127 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
124 128 def test_diff_side_by_side_from_0_commit(self, app, backend, backend_stub):
125 129 f_path = 'test_sidebyside_file.py'
126 130 commit1_content = 'content-25d7e49c18b159446c\n'
127 131 commit2_content = 'content-603d6c72c46d953420\n'
128 132 repo = backend.create_repo()
129 133
130 134 commit1 = commit_change(
131 135 repo.repo_name, filename=f_path, content=commit1_content,
132 136 message='A', vcs_type=backend.alias, parent=None, newfile=True)
133 137
134 138 commit2 = commit_change(
135 139 repo.repo_name, filename=f_path, content=commit2_content,
136 140 message='B, child of A', vcs_type=backend.alias, parent=commit1)
137 141
138 142 response = self.app.get(route_path(
139 143 'repo_compare',
140 144 repo_name=repo.repo_name,
141 145 source_ref_type='rev',
142 146 source_ref=EmptyCommit().raw_id,
143 147 target_ref_type='rev',
144 148 target_ref=commit2.raw_id,
145 149 params=dict(diffmode='sidebyside')
146 150 ))
147 151
148 152 response.mustcontain('Expand 2 commits')
149 153 response.mustcontain('123 file changed')
150 154
151 155 response.mustcontain(
152 156 'r%s:%s...r%s:%s' % (
153 157 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
154 158
155 response.mustcontain('<strong>{}</strong>'.format(f_path))
159 response.mustcontain(f_path)
156 160
157 161 @pytest.mark.xfail(reason='GIT does not handle empty commit compare correct (missing 1 commit)')
158 162 def test_diff_side_by_side_from_0_commit_with_file_filter(self, app, backend, backend_stub):
159 163 f_path = 'test_sidebyside_file.py'
160 164 commit1_content = 'content-25d7e49c18b159446c\n'
161 165 commit2_content = 'content-603d6c72c46d953420\n'
162 166 repo = backend.create_repo()
163 167
164 168 commit1 = commit_change(
165 169 repo.repo_name, filename=f_path, content=commit1_content,
166 170 message='A', vcs_type=backend.alias, parent=None, newfile=True)
167 171
168 172 commit2 = commit_change(
169 173 repo.repo_name, filename=f_path, content=commit2_content,
170 174 message='B, child of A', vcs_type=backend.alias, parent=commit1)
171 175
172 176 response = self.app.get(route_path(
173 177 'repo_compare',
174 178 repo_name=repo.repo_name,
175 179 source_ref_type='rev',
176 180 source_ref=EmptyCommit().raw_id,
177 181 target_ref_type='rev',
178 182 target_ref=commit2.raw_id,
179 183 params=dict(f_path=f_path, target_repo=repo.repo_name, diffmode='sidebyside')
180 184 ))
181 185
182 186 response.mustcontain('Expand 2 commits')
183 187 response.mustcontain('1 file changed')
184 188
185 189 response.mustcontain(
186 190 'r%s:%s...r%s:%s' % (
187 191 commit1.idx, commit1.short_id, commit2.idx, commit2.short_id))
188 192
189 response.mustcontain('<strong>{}</strong>'.format(f_path))
193 response.mustcontain(f_path)
190 194
191 195 def test_diff_side_by_side_with_empty_file(self, app, backend, backend_stub):
192 196 commits = [
193 197 {'message': 'First commit'},
194 198 {'message': 'Second commit'},
195 199 {'message': 'Commit with binary',
196 200 'added': [nodes.FileNode('file.empty', content='')]},
197 201 ]
198 202 f_path = 'file.empty'
199 203 repo = backend.create_repo(commits=commits)
200 204 commit1 = repo.get_commit(commit_idx=0)
201 205 commit2 = repo.get_commit(commit_idx=1)
202 206 commit3 = repo.get_commit(commit_idx=2)
203 207
204 208 response = self.app.get(route_path(
205 209 'repo_compare',
206 210 repo_name=repo.repo_name,
207 211 source_ref_type='rev',
208 212 source_ref=commit1.raw_id,
209 213 target_ref_type='rev',
210 214 target_ref=commit3.raw_id,
211 215 params=dict(f_path=f_path, target_repo=repo.repo_name, diffmode='sidebyside')
212 216 ))
213 217
214 218 response.mustcontain('Expand 2 commits')
215 219 response.mustcontain('1 file changed')
216 220
217 221 response.mustcontain(
218 222 'r%s:%s...r%s:%s' % (
219 223 commit2.idx, commit2.short_id, commit3.idx, commit3.short_id))
220 224
221 response.mustcontain('<strong>{}</strong>'.format(f_path))
225 response.mustcontain(f_path)
222 226
223 227 def test_diff_sidebyside_two_commits_with_file_filter(self, app, backend):
224 228 commit_id_range = {
225 229 'hg': {
226 230 'commits': ['4fdd71e9427417b2e904e0464c634fdee85ec5a7',
227 231 '603d6c72c46d953420c89d36372f08d9f305f5dd'],
228 'changes': '1 file changed: 3 inserted, 3 deleted'
232 'changes': (1, 3, 3)
229 233 },
230 234 'git': {
231 235 'commits': ['f5fbf9cfd5f1f1be146f6d3b38bcd791a7480c13',
232 236 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'],
233 'changes': '1 file changed: 3 inserted, 3 deleted'
237 'changes': (1, 3, 3)
234 238 },
235 239
236 240 'svn': {
237 241 'commits': ['335',
238 242 '337'],
239 'changes': '1 file changed: 3 inserted, 3 deleted'
243 'changes': (1, 3, 3)
240 244 },
241 245 }
242 246 f_path = 'docs/conf.py'
243 247
244 248 commit_info = commit_id_range[backend.alias]
245 249 commit2, commit1 = commit_info['commits']
246 250 file_changes = commit_info['changes']
247 251
248 252 response = self.app.get(route_path(
249 253 'repo_compare',
250 254 repo_name=backend.repo_name,
251 255 source_ref_type='rev',
252 256 source_ref=commit2,
253 257 target_ref_type='rev',
254 258 target_ref=commit1,
255 259 params=dict(f_path=f_path, target_repo=backend.repo_name, diffmode='sidebyside')
256 260 ))
257 261
258 262 response.mustcontain('Expand 2 commits')
259 response.mustcontain(file_changes)
263
264 compare_page = ComparePage(response)
265 compare_page.contains_change_summary(*file_changes)
@@ -1,1066 +1,1070 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 os
22 22
23 23 import mock
24 24 import pytest
25 25
26 from rhodecode.apps.repository.tests.test_repo_compare import ComparePage
26 27 from rhodecode.apps.repository.views.repo_files import RepoFilesView
27 28 from rhodecode.lib import helpers as h
28 29 from rhodecode.lib.compat import OrderedDict
29 30 from rhodecode.lib.ext_json import json
30 31 from rhodecode.lib.vcs import nodes
31 32
32 33 from rhodecode.lib.vcs.conf import settings
33 34 from rhodecode.tests import assert_session_flash
34 35 from rhodecode.tests.fixture import Fixture
35 36 from rhodecode.model.db import Session
36 37
37 38 fixture = Fixture()
38 39
39 40
40 41 def get_node_history(backend_type):
41 42 return {
42 43 'hg': json.loads(fixture.load_resource('hg_node_history_response.json')),
43 44 'git': json.loads(fixture.load_resource('git_node_history_response.json')),
44 45 'svn': json.loads(fixture.load_resource('svn_node_history_response.json')),
45 46 }[backend_type]
46 47
47 48
48 49 def route_path(name, params=None, **kwargs):
49 50 import urllib
50 51
51 52 base_url = {
52 53 'repo_summary': '/{repo_name}',
53 54 'repo_archivefile': '/{repo_name}/archive/{fname}',
54 55 'repo_files_diff': '/{repo_name}/diff/{f_path}',
55 56 'repo_files_diff_2way_redirect': '/{repo_name}/diff-2way/{f_path}',
56 57 'repo_files': '/{repo_name}/files/{commit_id}/{f_path}',
57 58 'repo_files:default_path': '/{repo_name}/files/{commit_id}/',
58 59 'repo_files:default_commit': '/{repo_name}/files',
59 60 'repo_files:rendered': '/{repo_name}/render/{commit_id}/{f_path}',
60 61 'repo_files:annotated': '/{repo_name}/annotate/{commit_id}/{f_path}',
61 62 'repo_files:annotated_previous': '/{repo_name}/annotate-previous/{commit_id}/{f_path}',
62 63 'repo_files_nodelist': '/{repo_name}/nodelist/{commit_id}/{f_path}',
63 64 'repo_file_raw': '/{repo_name}/raw/{commit_id}/{f_path}',
64 65 'repo_file_download': '/{repo_name}/download/{commit_id}/{f_path}',
65 66 'repo_file_history': '/{repo_name}/history/{commit_id}/{f_path}',
66 67 'repo_file_authors': '/{repo_name}/authors/{commit_id}/{f_path}',
67 68 'repo_files_remove_file': '/{repo_name}/remove_file/{commit_id}/{f_path}',
68 69 'repo_files_delete_file': '/{repo_name}/delete_file/{commit_id}/{f_path}',
69 70 'repo_files_edit_file': '/{repo_name}/edit_file/{commit_id}/{f_path}',
70 71 'repo_files_update_file': '/{repo_name}/update_file/{commit_id}/{f_path}',
71 72 'repo_files_add_file': '/{repo_name}/add_file/{commit_id}/{f_path}',
72 73 'repo_files_create_file': '/{repo_name}/create_file/{commit_id}/{f_path}',
73 74 'repo_nodetree_full': '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
74 75 'repo_nodetree_full:default_path': '/{repo_name}/nodetree_full/{commit_id}/',
75 76 }[name].format(**kwargs)
76 77
77 78 if params:
78 79 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
79 80 return base_url
80 81
81 82
82 83 def assert_files_in_response(response, files, params):
83 84 template = (
84 85 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
85 86 _assert_items_in_response(response, files, template, params)
86 87
87 88
88 89 def assert_dirs_in_response(response, dirs, params):
89 90 template = (
90 91 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
91 92 _assert_items_in_response(response, dirs, template, params)
92 93
93 94
94 95 def _assert_items_in_response(response, items, template, params):
95 96 for item in items:
96 97 item_params = {'name': item}
97 98 item_params.update(params)
98 99 response.mustcontain(template % item_params)
99 100
100 101
101 102 def assert_timeago_in_response(response, items, params):
102 103 for item in items:
103 104 response.mustcontain(h.age_component(params['date']))
104 105
105 106
106 107 @pytest.mark.usefixtures("app")
107 108 class TestFilesViews(object):
108 109
109 110 def test_show_files(self, backend):
110 111 response = self.app.get(
111 112 route_path('repo_files',
112 113 repo_name=backend.repo_name,
113 114 commit_id='tip', f_path='/'))
114 115 commit = backend.repo.get_commit()
115 116
116 117 params = {
117 118 'repo_name': backend.repo_name,
118 119 'commit_id': commit.raw_id,
119 120 'date': commit.date
120 121 }
121 122 assert_dirs_in_response(response, ['docs', 'vcs'], params)
122 123 files = [
123 124 '.gitignore',
124 125 '.hgignore',
125 126 '.hgtags',
126 127 # TODO: missing in Git
127 128 # '.travis.yml',
128 129 'MANIFEST.in',
129 130 'README.rst',
130 131 # TODO: File is missing in svn repository
131 132 # 'run_test_and_report.sh',
132 133 'setup.cfg',
133 134 'setup.py',
134 135 'test_and_report.sh',
135 136 'tox.ini',
136 137 ]
137 138 assert_files_in_response(response, files, params)
138 139 assert_timeago_in_response(response, files, params)
139 140
140 141 def test_show_files_links_submodules_with_absolute_url(self, backend_hg):
141 142 repo = backend_hg['subrepos']
142 143 response = self.app.get(
143 144 route_path('repo_files',
144 145 repo_name=repo.repo_name,
145 146 commit_id='tip', f_path='/'))
146 147 assert_response = response.assert_response()
147 148 assert_response.contains_one_link(
148 149 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
149 150
150 151 def test_show_files_links_submodules_with_absolute_url_subpaths(
151 152 self, backend_hg):
152 153 repo = backend_hg['subrepos']
153 154 response = self.app.get(
154 155 route_path('repo_files',
155 156 repo_name=repo.repo_name,
156 157 commit_id='tip', f_path='/'))
157 158 assert_response = response.assert_response()
158 159 assert_response.contains_one_link(
159 160 'subpaths-path @ 000000000000',
160 161 'http://sub-base.example.com/subpaths-path')
161 162
162 163 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
163 164 def test_files_menu(self, backend):
164 165 new_branch = "temp_branch_name"
165 166 commits = [
166 167 {'message': 'a'},
167 168 {'message': 'b', 'branch': new_branch}
168 169 ]
169 170 backend.create_repo(commits)
170 171 backend.repo.landing_rev = "branch:%s" % new_branch
171 172 Session().commit()
172 173
173 174 # get response based on tip and not new commit
174 175 response = self.app.get(
175 176 route_path('repo_files',
176 177 repo_name=backend.repo_name,
177 178 commit_id='tip', f_path='/'))
178 179
179 180 # make sure Files menu url is not tip but new commit
180 181 landing_rev = backend.repo.landing_rev[1]
181 182 files_url = route_path('repo_files:default_path',
182 183 repo_name=backend.repo_name,
183 184 commit_id=landing_rev)
184 185
185 186 assert landing_rev != 'tip'
186 187 response.mustcontain(
187 188 '<li class="active"><a class="menulink" href="%s">' % files_url)
188 189
189 190 def test_show_files_commit(self, backend):
190 191 commit = backend.repo.get_commit(commit_idx=32)
191 192
192 193 response = self.app.get(
193 194 route_path('repo_files',
194 195 repo_name=backend.repo_name,
195 196 commit_id=commit.raw_id, f_path='/'))
196 197
197 198 dirs = ['docs', 'tests']
198 199 files = ['README.rst']
199 200 params = {
200 201 'repo_name': backend.repo_name,
201 202 'commit_id': commit.raw_id,
202 203 }
203 204 assert_dirs_in_response(response, dirs, params)
204 205 assert_files_in_response(response, files, params)
205 206
206 207 def test_show_files_different_branch(self, backend):
207 208 branches = dict(
208 209 hg=(150, ['git']),
209 210 # TODO: Git test repository does not contain other branches
210 211 git=(633, ['master']),
211 212 # TODO: Branch support in Subversion
212 213 svn=(150, [])
213 214 )
214 215 idx, branches = branches[backend.alias]
215 216 commit = backend.repo.get_commit(commit_idx=idx)
216 217 response = self.app.get(
217 218 route_path('repo_files',
218 219 repo_name=backend.repo_name,
219 220 commit_id=commit.raw_id, f_path='/'))
220 221
221 222 assert_response = response.assert_response()
222 223 for branch in branches:
223 224 assert_response.element_contains('.tags .branchtag', branch)
224 225
225 226 def test_show_files_paging(self, backend):
226 227 repo = backend.repo
227 228 indexes = [73, 92, 109, 1, 0]
228 229 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id)
229 230 for rev in indexes]
230 231
231 232 for idx in idx_map:
232 233 response = self.app.get(
233 234 route_path('repo_files',
234 235 repo_name=backend.repo_name,
235 236 commit_id=idx[1], f_path='/'))
236 237
237 238 response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8]))
238 239
239 240 def test_file_source(self, backend):
240 241 commit = backend.repo.get_commit(commit_idx=167)
241 242 response = self.app.get(
242 243 route_path('repo_files',
243 244 repo_name=backend.repo_name,
244 245 commit_id=commit.raw_id, f_path='vcs/nodes.py'))
245 246
246 247 msgbox = """<div class="commit">%s</div>"""
247 248 response.mustcontain(msgbox % (commit.message, ))
248 249
249 250 assert_response = response.assert_response()
250 251 if commit.branch:
251 252 assert_response.element_contains(
252 253 '.tags.tags-main .branchtag', commit.branch)
253 254 if commit.tags:
254 255 for tag in commit.tags:
255 256 assert_response.element_contains('.tags.tags-main .tagtag', tag)
256 257
257 258 def test_file_source_annotated(self, backend):
258 259 response = self.app.get(
259 260 route_path('repo_files:annotated',
260 261 repo_name=backend.repo_name,
261 262 commit_id='tip', f_path='vcs/nodes.py'))
262 263 expected_commits = {
263 264 'hg': 'r356',
264 265 'git': 'r345',
265 266 'svn': 'r208',
266 267 }
267 268 response.mustcontain(expected_commits[backend.alias])
268 269
269 270 def test_file_source_authors(self, backend):
270 271 response = self.app.get(
271 272 route_path('repo_file_authors',
272 273 repo_name=backend.repo_name,
273 274 commit_id='tip', f_path='vcs/nodes.py'))
274 275 expected_authors = {
275 276 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
276 277 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
277 278 'svn': ('marcin', 'lukasz'),
278 279 }
279 280
280 281 for author in expected_authors[backend.alias]:
281 282 response.mustcontain(author)
282 283
283 284 def test_file_source_authors_with_annotation(self, backend):
284 285 response = self.app.get(
285 286 route_path('repo_file_authors',
286 287 repo_name=backend.repo_name,
287 288 commit_id='tip', f_path='vcs/nodes.py',
288 289 params=dict(annotate=1)))
289 290 expected_authors = {
290 291 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
291 292 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
292 293 'svn': ('marcin', 'lukasz'),
293 294 }
294 295
295 296 for author in expected_authors[backend.alias]:
296 297 response.mustcontain(author)
297 298
298 299 def test_file_source_history(self, backend, xhr_header):
299 300 response = self.app.get(
300 301 route_path('repo_file_history',
301 302 repo_name=backend.repo_name,
302 303 commit_id='tip', f_path='vcs/nodes.py'),
303 304 extra_environ=xhr_header)
304 305 assert get_node_history(backend.alias) == json.loads(response.body)
305 306
306 307 def test_file_source_history_svn(self, backend_svn, xhr_header):
307 308 simple_repo = backend_svn['svn-simple-layout']
308 309 response = self.app.get(
309 310 route_path('repo_file_history',
310 311 repo_name=simple_repo.repo_name,
311 312 commit_id='tip', f_path='trunk/example.py'),
312 313 extra_environ=xhr_header)
313 314
314 315 expected_data = json.loads(
315 316 fixture.load_resource('svn_node_history_branches.json'))
316 317
317 318 assert expected_data == response.json
318 319
319 320 def test_file_source_history_with_annotation(self, backend, xhr_header):
320 321 response = self.app.get(
321 322 route_path('repo_file_history',
322 323 repo_name=backend.repo_name,
323 324 commit_id='tip', f_path='vcs/nodes.py',
324 325 params=dict(annotate=1)),
325 326
326 327 extra_environ=xhr_header)
327 328 assert get_node_history(backend.alias) == json.loads(response.body)
328 329
329 330 def test_tree_search_top_level(self, backend, xhr_header):
330 331 commit = backend.repo.get_commit(commit_idx=173)
331 332 response = self.app.get(
332 333 route_path('repo_files_nodelist',
333 334 repo_name=backend.repo_name,
334 335 commit_id=commit.raw_id, f_path='/'),
335 336 extra_environ=xhr_header)
336 337 assert 'nodes' in response.json
337 338 assert {'name': 'docs', 'type': 'dir'} in response.json['nodes']
338 339
339 340 def test_tree_search_missing_xhr(self, backend):
340 341 self.app.get(
341 342 route_path('repo_files_nodelist',
342 343 repo_name=backend.repo_name,
343 344 commit_id='tip', f_path='/'),
344 345 status=404)
345 346
346 347 def test_tree_search_at_path(self, backend, xhr_header):
347 348 commit = backend.repo.get_commit(commit_idx=173)
348 349 response = self.app.get(
349 350 route_path('repo_files_nodelist',
350 351 repo_name=backend.repo_name,
351 352 commit_id=commit.raw_id, f_path='/docs'),
352 353 extra_environ=xhr_header)
353 354 assert 'nodes' in response.json
354 355 nodes = response.json['nodes']
355 356 assert {'name': 'docs/api', 'type': 'dir'} in nodes
356 357 assert {'name': 'docs/index.rst', 'type': 'file'} in nodes
357 358
358 359 def test_tree_search_at_path_2nd_level(self, backend, xhr_header):
359 360 commit = backend.repo.get_commit(commit_idx=173)
360 361 response = self.app.get(
361 362 route_path('repo_files_nodelist',
362 363 repo_name=backend.repo_name,
363 364 commit_id=commit.raw_id, f_path='/docs/api'),
364 365 extra_environ=xhr_header)
365 366 assert 'nodes' in response.json
366 367 nodes = response.json['nodes']
367 368 assert {'name': 'docs/api/index.rst', 'type': 'file'} in nodes
368 369
369 370 def test_tree_search_at_path_missing_xhr(self, backend):
370 371 self.app.get(
371 372 route_path('repo_files_nodelist',
372 373 repo_name=backend.repo_name,
373 374 commit_id='tip', f_path='/docs'),
374 375 status=404)
375 376
376 377 def test_nodetree(self, backend, xhr_header):
377 378 commit = backend.repo.get_commit(commit_idx=173)
378 379 response = self.app.get(
379 380 route_path('repo_nodetree_full',
380 381 repo_name=backend.repo_name,
381 382 commit_id=commit.raw_id, f_path='/'),
382 383 extra_environ=xhr_header)
383 384
384 385 assert_response = response.assert_response()
385 386
386 387 for attr in ['data-commit-id', 'data-date', 'data-author']:
387 388 elements = assert_response.get_elements('[{}]'.format(attr))
388 389 assert len(elements) > 1
389 390
390 391 for element in elements:
391 392 assert element.get(attr)
392 393
393 394 def test_nodetree_if_file(self, backend, xhr_header):
394 395 commit = backend.repo.get_commit(commit_idx=173)
395 396 response = self.app.get(
396 397 route_path('repo_nodetree_full',
397 398 repo_name=backend.repo_name,
398 399 commit_id=commit.raw_id, f_path='README.rst'),
399 400 extra_environ=xhr_header)
400 401 assert response.body == ''
401 402
402 403 def test_nodetree_wrong_path(self, backend, xhr_header):
403 404 commit = backend.repo.get_commit(commit_idx=173)
404 405 response = self.app.get(
405 406 route_path('repo_nodetree_full',
406 407 repo_name=backend.repo_name,
407 408 commit_id=commit.raw_id, f_path='/dont-exist'),
408 409 extra_environ=xhr_header)
409 410
410 411 err = 'error: There is no file nor ' \
411 412 'directory at the given path'
412 413 assert err in response.body
413 414
414 415 def test_nodetree_missing_xhr(self, backend):
415 416 self.app.get(
416 417 route_path('repo_nodetree_full',
417 418 repo_name=backend.repo_name,
418 419 commit_id='tip', f_path='/'),
419 420 status=404)
420 421
421 422
422 423 @pytest.mark.usefixtures("app", "autologin_user")
423 424 class TestRawFileHandling(object):
424 425
425 426 def test_download_file(self, backend):
426 427 commit = backend.repo.get_commit(commit_idx=173)
427 428 response = self.app.get(
428 429 route_path('repo_file_download',
429 430 repo_name=backend.repo_name,
430 431 commit_id=commit.raw_id, f_path='vcs/nodes.py'),)
431 432
432 433 assert response.content_disposition == 'attachment; filename="nodes.py"; filename*=UTF-8\'\'nodes.py'
433 434 assert response.content_type == "text/x-python"
434 435
435 436 def test_download_file_wrong_cs(self, backend):
436 437 raw_id = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
437 438
438 439 response = self.app.get(
439 440 route_path('repo_file_download',
440 441 repo_name=backend.repo_name,
441 442 commit_id=raw_id, f_path='vcs/nodes.svg'),
442 443 status=404)
443 444
444 445 msg = """No such commit exists for this repository"""
445 446 response.mustcontain(msg)
446 447
447 448 def test_download_file_wrong_f_path(self, backend):
448 449 commit = backend.repo.get_commit(commit_idx=173)
449 450 f_path = 'vcs/ERRORnodes.py'
450 451
451 452 response = self.app.get(
452 453 route_path('repo_file_download',
453 454 repo_name=backend.repo_name,
454 455 commit_id=commit.raw_id, f_path=f_path),
455 456 status=404)
456 457
457 458 msg = (
458 459 "There is no file nor directory at the given path: "
459 460 "`%s` at commit %s" % (f_path, commit.short_id))
460 461 response.mustcontain(msg)
461 462
462 463 def test_file_raw(self, backend):
463 464 commit = backend.repo.get_commit(commit_idx=173)
464 465 response = self.app.get(
465 466 route_path('repo_file_raw',
466 467 repo_name=backend.repo_name,
467 468 commit_id=commit.raw_id, f_path='vcs/nodes.py'),)
468 469
469 470 assert response.content_type == "text/plain"
470 471
471 472 def test_file_raw_binary(self, backend):
472 473 commit = backend.repo.get_commit()
473 474 response = self.app.get(
474 475 route_path('repo_file_raw',
475 476 repo_name=backend.repo_name,
476 477 commit_id=commit.raw_id,
477 478 f_path='docs/theme/ADC/static/breadcrumb_background.png'),)
478 479
479 480 assert response.content_disposition == 'inline'
480 481
481 482 def test_raw_file_wrong_cs(self, backend):
482 483 raw_id = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
483 484
484 485 response = self.app.get(
485 486 route_path('repo_file_raw',
486 487 repo_name=backend.repo_name,
487 488 commit_id=raw_id, f_path='vcs/nodes.svg'),
488 489 status=404)
489 490
490 491 msg = """No such commit exists for this repository"""
491 492 response.mustcontain(msg)
492 493
493 494 def test_raw_wrong_f_path(self, backend):
494 495 commit = backend.repo.get_commit(commit_idx=173)
495 496 f_path = 'vcs/ERRORnodes.py'
496 497 response = self.app.get(
497 498 route_path('repo_file_raw',
498 499 repo_name=backend.repo_name,
499 500 commit_id=commit.raw_id, f_path=f_path),
500 501 status=404)
501 502
502 503 msg = (
503 504 "There is no file nor directory at the given path: "
504 505 "`%s` at commit %s" % (f_path, commit.short_id))
505 506 response.mustcontain(msg)
506 507
507 508 def test_raw_svg_should_not_be_rendered(self, backend):
508 509 backend.create_repo()
509 510 backend.ensure_file("xss.svg")
510 511 response = self.app.get(
511 512 route_path('repo_file_raw',
512 513 repo_name=backend.repo_name,
513 514 commit_id='tip', f_path='xss.svg'),)
514 515 # If the content type is image/svg+xml then it allows to render HTML
515 516 # and malicious SVG.
516 517 assert response.content_type == "text/plain"
517 518
518 519
519 520 @pytest.mark.usefixtures("app")
520 521 class TestRepositoryArchival(object):
521 522
522 523 def test_archival(self, backend):
523 524 backend.enable_downloads()
524 525 commit = backend.repo.get_commit(commit_idx=173)
525 526 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
526 527
527 528 short = commit.short_id + extension
528 529 fname = commit.raw_id + extension
529 530 filename = '%s-%s' % (backend.repo_name, short)
530 531 response = self.app.get(
531 532 route_path('repo_archivefile',
532 533 repo_name=backend.repo_name,
533 534 fname=fname))
534 535
535 536 assert response.status == '200 OK'
536 537 headers = [
537 538 ('Content-Disposition', 'attachment; filename=%s' % filename),
538 539 ('Content-Type', '%s' % content_type),
539 540 ]
540 541
541 542 for header in headers:
542 543 assert header in response.headers.items()
543 544
544 545 @pytest.mark.parametrize('arch_ext',[
545 546 'tar', 'rar', 'x', '..ax', '.zipz', 'tar.gz.tar'])
546 547 def test_archival_wrong_ext(self, backend, arch_ext):
547 548 backend.enable_downloads()
548 549 commit = backend.repo.get_commit(commit_idx=173)
549 550
550 551 fname = commit.raw_id + '.' + arch_ext
551 552
552 553 response = self.app.get(
553 554 route_path('repo_archivefile',
554 555 repo_name=backend.repo_name,
555 556 fname=fname))
556 557 response.mustcontain(
557 558 'Unknown archive type for: `{}`'.format(fname))
558 559
559 560 @pytest.mark.parametrize('commit_id', [
560 561 '00x000000', 'tar', 'wrong', '@$@$42413232', '232dffcd'])
561 562 def test_archival_wrong_commit_id(self, backend, commit_id):
562 563 backend.enable_downloads()
563 564 fname = '%s.zip' % commit_id
564 565
565 566 response = self.app.get(
566 567 route_path('repo_archivefile',
567 568 repo_name=backend.repo_name,
568 569 fname=fname))
569 570 response.mustcontain('Unknown commit_id')
570 571
571 572
572 573 @pytest.mark.usefixtures("app")
573 574 class TestFilesDiff(object):
574 575
575 576 @pytest.mark.parametrize("diff", ['diff', 'download', 'raw'])
576 577 def test_file_full_diff(self, backend, diff):
577 578 commit1 = backend.repo.get_commit(commit_idx=-1)
578 579 commit2 = backend.repo.get_commit(commit_idx=-2)
579 580
580 581 response = self.app.get(
581 582 route_path('repo_files_diff',
582 583 repo_name=backend.repo_name,
583 584 f_path='README'),
584 585 params={
585 586 'diff1': commit2.raw_id,
586 587 'diff2': commit1.raw_id,
587 588 'fulldiff': '1',
588 589 'diff': diff,
589 590 })
590 591
591 592 if diff == 'diff':
592 593 # use redirect since this is OLD view redirecting to compare page
593 594 response = response.follow()
594 595
595 596 # It's a symlink to README.rst
596 597 response.mustcontain('README.rst')
597 598 response.mustcontain('No newline at end of file')
598 599
599 600 def test_file_binary_diff(self, backend):
600 601 commits = [
601 602 {'message': 'First commit'},
602 603 {'message': 'Commit with binary',
603 604 'added': [nodes.FileNode('file.bin', content='\0BINARY\0')]},
604 605 ]
605 606 repo = backend.create_repo(commits=commits)
606 607
607 608 response = self.app.get(
608 609 route_path('repo_files_diff',
609 610 repo_name=backend.repo_name,
610 611 f_path='file.bin'),
611 612 params={
612 613 'diff1': repo.get_commit(commit_idx=0).raw_id,
613 614 'diff2': repo.get_commit(commit_idx=1).raw_id,
614 615 'fulldiff': '1',
615 616 'diff': 'diff',
616 617 })
617 618 # use redirect since this is OLD view redirecting to compare page
618 619 response = response.follow()
619 620 response.mustcontain('Expand 1 commit')
620 response.mustcontain('1 file changed: 0 inserted, 0 deleted')
621 file_changes = (1, 0, 0)
622
623 compare_page = ComparePage(response)
624 compare_page.contains_change_summary(*file_changes)
621 625
622 626 if backend.alias == 'svn':
623 627 response.mustcontain('new file 10644')
624 628 # TODO(marcink): SVN doesn't yet detect binary changes
625 629 else:
626 630 response.mustcontain('new file 100644')
627 631 response.mustcontain('binary diff hidden')
628 632
629 633 def test_diff_2way(self, backend):
630 634 commit1 = backend.repo.get_commit(commit_idx=-1)
631 635 commit2 = backend.repo.get_commit(commit_idx=-2)
632 636 response = self.app.get(
633 637 route_path('repo_files_diff_2way_redirect',
634 638 repo_name=backend.repo_name,
635 639 f_path='README'),
636 640 params={
637 641 'diff1': commit2.raw_id,
638 642 'diff2': commit1.raw_id,
639 643 })
640 644 # use redirect since this is OLD view redirecting to compare page
641 645 response = response.follow()
642 646
643 647 # It's a symlink to README.rst
644 648 response.mustcontain('README.rst')
645 649 response.mustcontain('No newline at end of file')
646 650
647 651 def test_requires_one_commit_id(self, backend, autologin_user):
648 652 response = self.app.get(
649 653 route_path('repo_files_diff',
650 654 repo_name=backend.repo_name,
651 655 f_path='README.rst'),
652 656 status=400)
653 657 response.mustcontain(
654 658 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
655 659
656 660 def test_returns_no_files_if_file_does_not_exist(self, vcsbackend):
657 661 repo = vcsbackend.repo
658 662 response = self.app.get(
659 663 route_path('repo_files_diff',
660 664 repo_name=repo.name,
661 665 f_path='does-not-exist-in-any-commit'),
662 666 params={
663 667 'diff1': repo[0].raw_id,
664 668 'diff2': repo[1].raw_id
665 669 })
666 670
667 671 response = response.follow()
668 672 response.mustcontain('No files')
669 673
670 674 def test_returns_redirect_if_file_not_changed(self, backend):
671 675 commit = backend.repo.get_commit(commit_idx=-1)
672 676 response = self.app.get(
673 677 route_path('repo_files_diff_2way_redirect',
674 678 repo_name=backend.repo_name,
675 679 f_path='README'),
676 680 params={
677 681 'diff1': commit.raw_id,
678 682 'diff2': commit.raw_id,
679 683 })
680 684
681 685 response = response.follow()
682 686 response.mustcontain('No files')
683 687 response.mustcontain('No commits in this compare')
684 688
685 689 def test_supports_diff_to_different_path_svn(self, backend_svn):
686 690 #TODO: check this case
687 691 return
688 692
689 693 repo = backend_svn['svn-simple-layout'].scm_instance()
690 694 commit_id_1 = '24'
691 695 commit_id_2 = '26'
692 696
693 697 response = self.app.get(
694 698 route_path('repo_files_diff',
695 699 repo_name=backend_svn.repo_name,
696 700 f_path='trunk/example.py'),
697 701 params={
698 702 'diff1': 'tags/v0.2/example.py@' + commit_id_1,
699 703 'diff2': commit_id_2,
700 704 })
701 705
702 706 response = response.follow()
703 707 response.mustcontain(
704 708 # diff contains this
705 709 "Will print out a useful message on invocation.")
706 710
707 711 # Note: Expecting that we indicate the user what's being compared
708 712 response.mustcontain("trunk/example.py")
709 713 response.mustcontain("tags/v0.2/example.py")
710 714
711 715 def test_show_rev_redirects_to_svn_path(self, backend_svn):
712 716 #TODO: check this case
713 717 return
714 718
715 719 repo = backend_svn['svn-simple-layout'].scm_instance()
716 720 commit_id = repo[-1].raw_id
717 721
718 722 response = self.app.get(
719 723 route_path('repo_files_diff',
720 724 repo_name=backend_svn.repo_name,
721 725 f_path='trunk/example.py'),
722 726 params={
723 727 'diff1': 'branches/argparse/example.py@' + commit_id,
724 728 'diff2': commit_id,
725 729 },
726 730 status=302)
727 731 response = response.follow()
728 732 assert response.headers['Location'].endswith(
729 733 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
730 734
731 735 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
732 736 #TODO: check this case
733 737 return
734 738
735 739 repo = backend_svn['svn-simple-layout'].scm_instance()
736 740 commit_id = repo[-1].raw_id
737 741 response = self.app.get(
738 742 route_path('repo_files_diff',
739 743 repo_name=backend_svn.repo_name,
740 744 f_path='trunk/example.py'),
741 745 params={
742 746 'diff1': 'branches/argparse/example.py@' + commit_id,
743 747 'diff2': commit_id,
744 748 'show_rev': 'Show at Revision',
745 749 'annotate': 'true',
746 750 },
747 751 status=302)
748 752 response = response.follow()
749 753 assert response.headers['Location'].endswith(
750 754 'svn-svn-simple-layout/annotate/26/branches/argparse/example.py')
751 755
752 756
753 757 @pytest.mark.usefixtures("app", "autologin_user")
754 758 class TestModifyFilesWithWebInterface(object):
755 759
756 760 def test_add_file_view(self, backend):
757 761 self.app.get(
758 762 route_path('repo_files_add_file',
759 763 repo_name=backend.repo_name,
760 764 commit_id='tip', f_path='/')
761 765 )
762 766
763 767 @pytest.mark.xfail_backends("svn", reason="Depends on online editing")
764 768 def test_add_file_into_repo_missing_content(self, backend, csrf_token):
765 769 backend.create_repo()
766 770 filename = 'init.py'
767 771 response = self.app.post(
768 772 route_path('repo_files_create_file',
769 773 repo_name=backend.repo_name,
770 774 commit_id='tip', f_path='/'),
771 775 params={
772 776 'content': "",
773 777 'filename': filename,
774 778 'csrf_token': csrf_token,
775 779 },
776 780 status=302)
777 781 expected_msg = 'Successfully committed new file `{}`'.format(os.path.join(filename))
778 782 assert_session_flash(response, expected_msg)
779 783
780 784 def test_add_file_into_repo_missing_filename(self, backend, csrf_token):
781 785 commit_id = backend.repo.get_commit().raw_id
782 786 response = self.app.post(
783 787 route_path('repo_files_create_file',
784 788 repo_name=backend.repo_name,
785 789 commit_id=commit_id, f_path='/'),
786 790 params={
787 791 'content': "foo",
788 792 'csrf_token': csrf_token,
789 793 },
790 794 status=302)
791 795
792 796 assert_session_flash(response, 'No filename specified')
793 797
794 798 def test_add_file_into_repo_errors_and_no_commits(
795 799 self, backend, csrf_token):
796 800 repo = backend.create_repo()
797 801 # Create a file with no filename, it will display an error but
798 802 # the repo has no commits yet
799 803 response = self.app.post(
800 804 route_path('repo_files_create_file',
801 805 repo_name=repo.repo_name,
802 806 commit_id='tip', f_path='/'),
803 807 params={
804 808 'content': "foo",
805 809 'csrf_token': csrf_token,
806 810 },
807 811 status=302)
808 812
809 813 assert_session_flash(response, 'No filename specified')
810 814
811 815 # Not allowed, redirect to the summary
812 816 redirected = response.follow()
813 817 summary_url = h.route_path('repo_summary', repo_name=repo.repo_name)
814 818
815 819 # As there are no commits, displays the summary page with the error of
816 820 # creating a file with no filename
817 821
818 822 assert redirected.request.path == summary_url
819 823
820 824 @pytest.mark.parametrize("filename, clean_filename", [
821 825 ('/abs/foo', 'abs/foo'),
822 826 ('../rel/foo', 'rel/foo'),
823 827 ('file/../foo/foo', 'file/foo/foo'),
824 828 ])
825 829 def test_add_file_into_repo_bad_filenames(self, filename, clean_filename, backend, csrf_token):
826 830 repo = backend.create_repo()
827 831 commit_id = repo.get_commit().raw_id
828 832
829 833 response = self.app.post(
830 834 route_path('repo_files_create_file',
831 835 repo_name=repo.repo_name,
832 836 commit_id=commit_id, f_path='/'),
833 837 params={
834 838 'content': "foo",
835 839 'filename': filename,
836 840 'csrf_token': csrf_token,
837 841 },
838 842 status=302)
839 843
840 844 expected_msg = 'Successfully committed new file `{}`'.format(clean_filename)
841 845 assert_session_flash(response, expected_msg)
842 846
843 847 @pytest.mark.parametrize("cnt, filename, content", [
844 848 (1, 'foo.txt', "Content"),
845 849 (2, 'dir/foo.rst', "Content"),
846 850 (3, 'dir/foo-second.rst', "Content"),
847 851 (4, 'rel/dir/foo.bar', "Content"),
848 852 ])
849 853 def test_add_file_into_empty_repo(self, cnt, filename, content, backend, csrf_token):
850 854 repo = backend.create_repo()
851 855 commit_id = repo.get_commit().raw_id
852 856 response = self.app.post(
853 857 route_path('repo_files_create_file',
854 858 repo_name=repo.repo_name,
855 859 commit_id=commit_id, f_path='/'),
856 860 params={
857 861 'content': content,
858 862 'filename': filename,
859 863 'csrf_token': csrf_token,
860 864 },
861 865 status=302)
862 866
863 867 expected_msg = 'Successfully committed new file `{}`'.format(filename)
864 868 assert_session_flash(response, expected_msg)
865 869
866 870 def test_edit_file_view(self, backend):
867 871 response = self.app.get(
868 872 route_path('repo_files_edit_file',
869 873 repo_name=backend.repo_name,
870 874 commit_id=backend.default_head_id,
871 875 f_path='vcs/nodes.py'),
872 876 status=200)
873 877 response.mustcontain("Module holding everything related to vcs nodes.")
874 878
875 879 def test_edit_file_view_not_on_branch(self, backend):
876 880 repo = backend.create_repo()
877 881 backend.ensure_file("vcs/nodes.py")
878 882
879 883 response = self.app.get(
880 884 route_path('repo_files_edit_file',
881 885 repo_name=repo.repo_name,
882 886 commit_id='tip',
883 887 f_path='vcs/nodes.py'),
884 888 status=302)
885 889 assert_session_flash(
886 890 response, 'Cannot modify file. Given commit `tip` is not head of a branch.')
887 891
888 892 def test_edit_file_view_commit_changes(self, backend, csrf_token):
889 893 repo = backend.create_repo()
890 894 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
891 895
892 896 response = self.app.post(
893 897 route_path('repo_files_update_file',
894 898 repo_name=repo.repo_name,
895 899 commit_id=backend.default_head_id,
896 900 f_path='vcs/nodes.py'),
897 901 params={
898 902 'content': "print 'hello world'",
899 903 'message': 'I committed',
900 904 'filename': "vcs/nodes.py",
901 905 'csrf_token': csrf_token,
902 906 },
903 907 status=302)
904 908 assert_session_flash(
905 909 response, 'Successfully committed changes to file `vcs/nodes.py`')
906 910 tip = repo.get_commit(commit_idx=-1)
907 911 assert tip.message == 'I committed'
908 912
909 913 def test_edit_file_view_commit_changes_default_message(self, backend,
910 914 csrf_token):
911 915 repo = backend.create_repo()
912 916 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
913 917
914 918 commit_id = (
915 919 backend.default_branch_name or
916 920 backend.repo.scm_instance().commit_ids[-1])
917 921
918 922 response = self.app.post(
919 923 route_path('repo_files_update_file',
920 924 repo_name=repo.repo_name,
921 925 commit_id=commit_id,
922 926 f_path='vcs/nodes.py'),
923 927 params={
924 928 'content': "print 'hello world'",
925 929 'message': '',
926 930 'filename': "vcs/nodes.py",
927 931 'csrf_token': csrf_token,
928 932 },
929 933 status=302)
930 934 assert_session_flash(
931 935 response, 'Successfully committed changes to file `vcs/nodes.py`')
932 936 tip = repo.get_commit(commit_idx=-1)
933 937 assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise'
934 938
935 939 def test_delete_file_view(self, backend):
936 940 self.app.get(
937 941 route_path('repo_files_remove_file',
938 942 repo_name=backend.repo_name,
939 943 commit_id=backend.default_head_id,
940 944 f_path='vcs/nodes.py'),
941 945 status=200)
942 946
943 947 def test_delete_file_view_not_on_branch(self, backend):
944 948 repo = backend.create_repo()
945 949 backend.ensure_file('vcs/nodes.py')
946 950
947 951 response = self.app.get(
948 952 route_path('repo_files_remove_file',
949 953 repo_name=repo.repo_name,
950 954 commit_id='tip',
951 955 f_path='vcs/nodes.py'),
952 956 status=302)
953 957 assert_session_flash(
954 958 response, 'Cannot modify file. Given commit `tip` is not head of a branch.')
955 959
956 960 def test_delete_file_view_commit_changes(self, backend, csrf_token):
957 961 repo = backend.create_repo()
958 962 backend.ensure_file("vcs/nodes.py")
959 963
960 964 response = self.app.post(
961 965 route_path('repo_files_delete_file',
962 966 repo_name=repo.repo_name,
963 967 commit_id=backend.default_head_id,
964 968 f_path='vcs/nodes.py'),
965 969 params={
966 970 'message': 'i commited',
967 971 'csrf_token': csrf_token,
968 972 },
969 973 status=302)
970 974 assert_session_flash(
971 975 response, 'Successfully deleted file `vcs/nodes.py`')
972 976
973 977
974 978 @pytest.mark.usefixtures("app")
975 979 class TestFilesViewOtherCases(object):
976 980
977 981 def test_access_empty_repo_redirect_to_summary_with_alert_write_perms(
978 982 self, backend_stub, autologin_regular_user, user_regular,
979 983 user_util):
980 984
981 985 repo = backend_stub.create_repo()
982 986 user_util.grant_user_permission_to_repo(
983 987 repo, user_regular, 'repository.write')
984 988 response = self.app.get(
985 989 route_path('repo_files',
986 990 repo_name=repo.repo_name,
987 991 commit_id='tip', f_path='/'))
988 992
989 993 repo_file_add_url = route_path(
990 994 'repo_files_add_file',
991 995 repo_name=repo.repo_name,
992 996 commit_id=0, f_path='')
993 997
994 998 assert_session_flash(
995 999 response,
996 1000 'There are no files yet. <a class="alert-link" '
997 1001 'href="{}">Click here to add a new file.</a>'
998 1002 .format(repo_file_add_url))
999 1003
1000 1004 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
1001 1005 self, backend_stub, autologin_regular_user):
1002 1006 repo = backend_stub.create_repo()
1003 1007 # init session for anon user
1004 1008 route_path('repo_summary', repo_name=repo.repo_name)
1005 1009
1006 1010 repo_file_add_url = route_path(
1007 1011 'repo_files_add_file',
1008 1012 repo_name=repo.repo_name,
1009 1013 commit_id=0, f_path='')
1010 1014
1011 1015 response = self.app.get(
1012 1016 route_path('repo_files',
1013 1017 repo_name=repo.repo_name,
1014 1018 commit_id='tip', f_path='/'))
1015 1019
1016 1020 assert_session_flash(response, no_=repo_file_add_url)
1017 1021
1018 1022 @pytest.mark.parametrize('file_node', [
1019 1023 'archive/file.zip',
1020 1024 'diff/my-file.txt',
1021 1025 'render.py',
1022 1026 'render',
1023 1027 'remove_file',
1024 1028 'remove_file/to-delete.txt',
1025 1029 ])
1026 1030 def test_file_names_equal_to_routes_parts(self, backend, file_node):
1027 1031 backend.create_repo()
1028 1032 backend.ensure_file(file_node)
1029 1033
1030 1034 self.app.get(
1031 1035 route_path('repo_files',
1032 1036 repo_name=backend.repo_name,
1033 1037 commit_id='tip', f_path=file_node),
1034 1038 status=200)
1035 1039
1036 1040
1037 1041 class TestAdjustFilePathForSvn(object):
1038 1042 """
1039 1043 SVN specific adjustments of node history in RepoFilesView.
1040 1044 """
1041 1045
1042 1046 def test_returns_path_relative_to_matched_reference(self):
1043 1047 repo = self._repo(branches=['trunk'])
1044 1048 self.assert_file_adjustment('trunk/file', 'file', repo)
1045 1049
1046 1050 def test_does_not_modify_file_if_no_reference_matches(self):
1047 1051 repo = self._repo(branches=['trunk'])
1048 1052 self.assert_file_adjustment('notes/file', 'notes/file', repo)
1049 1053
1050 1054 def test_does_not_adjust_partial_directory_names(self):
1051 1055 repo = self._repo(branches=['trun'])
1052 1056 self.assert_file_adjustment('trunk/file', 'trunk/file', repo)
1053 1057
1054 1058 def test_is_robust_to_patterns_which_prefix_other_patterns(self):
1055 1059 repo = self._repo(branches=['trunk', 'trunk/new', 'trunk/old'])
1056 1060 self.assert_file_adjustment('trunk/new/file', 'file', repo)
1057 1061
1058 1062 def assert_file_adjustment(self, f_path, expected, repo):
1059 1063 result = RepoFilesView.adjust_file_path_for_svn(f_path, repo)
1060 1064 assert result == expected
1061 1065
1062 1066 def _repo(self, branches=None):
1063 1067 repo = mock.Mock()
1064 1068 repo.branches = OrderedDict((name, '0') for name in branches or [])
1065 1069 repo.tags = {}
1066 1070 return repo
General Comments 0
You need to be logged in to leave comments. Login now