blob: db047c36974c72052b731cf1674a3d5817e3c377 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Unittest for issue tracker views."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import logging
11import unittest
12
Adrià Vilanova Martínez9f9ade52022-10-10 23:20:11 +020013try:
14 from mox3 import mox
15except ImportError:
16 import mox
Copybara854996b2021-09-07 19:36:02 +000017
18from google.appengine.api import app_identity
19import ezt
20
21from framework import framework_views
22from framework import gcs_helpers
23from framework import template_helpers
24from framework import urls
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010025from mrproto import project_pb2
26from mrproto import tracker_pb2
Copybara854996b2021-09-07 19:36:02 +000027from services import service_manager
28from testing import fake
29from testing import testing_helpers
30from tracker import attachment_helpers
31from tracker import tracker_bizobj
32from tracker import tracker_constants
33from tracker import tracker_helpers
34from tracker import tracker_views
35
36
37def _Issue(project_name, local_id, summary, status):
38 issue = tracker_pb2.Issue()
39 issue.project_name = project_name
40 issue.local_id = local_id
41 issue.issue_id = 100000 + local_id
42 issue.summary = summary
43 issue.status = status
44 return issue
45
46
47def _MakeConfig():
48 config = tracker_pb2.ProjectIssueConfig()
49 config.well_known_labels = [
50 tracker_pb2.LabelDef(
51 label='Priority-High', label_docstring='Must be resolved'),
52 tracker_pb2.LabelDef(
53 label='Priority-Low', label_docstring='Can be slipped'),
54 ]
55 config.well_known_statuses.append(tracker_pb2.StatusDef(
56 status='New', means_open=True))
57 config.well_known_statuses.append(tracker_pb2.StatusDef(
58 status='Old', means_open=False))
59 return config
60
61
62class IssueViewTest(unittest.TestCase):
63
64 def setUp(self):
65 self.issue1 = _Issue('proj', 1, 'not too long summary', 'New')
66 self.issue2 = _Issue('proj', 2, 'sum 2', '')
67 self.issue3 = _Issue('proj', 3, 'sum 3', '')
68 self.issue4 = _Issue('proj', 4, 'sum 4', '')
69
70 self.issue1.reporter_id = 1002
71 self.issue1.owner_id = 2002
72 self.issue1.labels.extend(['A', 'B'])
73 self.issue1.derived_labels.extend(['C', 'D'])
74
75 self.issue2.reporter_id = 2002
76 self.issue2.labels.extend(['foo', 'bar'])
77 self.issue2.blocked_on_iids.extend(
78 [self.issue1.issue_id, self.issue3.issue_id])
79 self.issue2.blocking_iids.extend(
80 [self.issue1.issue_id, self.issue4.issue_id])
81 dref = tracker_pb2.DanglingIssueRef()
82 dref.project = 'codesite'
83 dref.issue_id = 5001
84 self.issue2.dangling_blocking_refs.append(dref)
85
86 self.issue3.reporter_id = 3002
87 self.issue3.labels.extend(['Hot'])
88
89 self.issue4.reporter_id = 3002
90 self.issue4.labels.extend(['Foo', 'Bar'])
91
92 self.restricted = _Issue('proj', 7, 'summary 7', '')
93 self.restricted.labels.extend([
94 'Restrict-View-Commit', 'Restrict-View-MyCustomPerm'])
95 self.restricted.derived_labels.extend([
96 'Restrict-AddIssueComment-Commit', 'Restrict-EditIssue-Commit',
97 'Restrict-Action-NeededPerm'])
98
99 self.users_by_id = {
100 0: 'user 0',
101 1002: 'user 1002',
102 2002: 'user 2002',
103 3002: 'user 3002',
104 4002: 'user 4002',
105 }
106
107 def CheckSimpleIssueView(self, config):
108 view1 = tracker_views.IssueView(
109 self.issue1, self.users_by_id, config)
110 self.assertEqual('not too long summary', view1.summary)
111 self.assertEqual('New', view1.status.name)
112 self.assertEqual('user 2002', view1.owner)
113 self.assertEqual('A', view1.labels[0].name)
114 self.assertEqual('B', view1.labels[1].name)
115 self.assertEqual('C', view1.derived_labels[0].name)
116 self.assertEqual('D', view1.derived_labels[1].name)
117 self.assertEqual([], view1.blocked_on)
118 self.assertEqual([], view1.blocking)
119 detail_url = '/p/%s%s?id=%d' % (
120 self.issue1.project_name, urls.ISSUE_DETAIL,
121 self.issue1.local_id)
122 self.assertEqual(detail_url, view1.detail_relative_url)
123 return view1
124
125 def testSimpleIssueView(self):
126 config = tracker_pb2.ProjectIssueConfig()
127 view1 = self.CheckSimpleIssueView(config)
128 self.assertEqual('', view1.status.docstring)
129
130 config.well_known_statuses.append(tracker_pb2.StatusDef(
131 status='New', status_docstring='Issue has not had review yet'))
132 view1 = self.CheckSimpleIssueView(config)
133 self.assertEqual('Issue has not had review yet',
134 view1.status.docstring)
135 self.assertIsNone(view1.restrictions.has_restrictions)
136 self.assertEqual('', view1.restrictions.view)
137 self.assertEqual('', view1.restrictions.add_comment)
138 self.assertEqual('', view1.restrictions.edit)
139
140 def testIsOpen(self):
141 config = _MakeConfig()
142 view1 = tracker_views.IssueView(
143 self.issue1, self.users_by_id, config)
144 self.assertEqual(ezt.boolean(True), view1.is_open)
145
146 self.issue1.status = 'Old'
147 view1 = tracker_views.IssueView(
148 self.issue1, self.users_by_id, config)
149 self.assertEqual(ezt.boolean(False), view1.is_open)
150
151 def testIssueViewWithRestrictions(self):
152 view = tracker_views.IssueView(
153 self.restricted, self.users_by_id, _MakeConfig())
154 self.assertTrue(view.restrictions.has_restrictions)
155 self.assertEqual('Commit and MyCustomPerm', view.restrictions.view)
156 self.assertEqual('Commit', view.restrictions.add_comment)
157 self.assertEqual('Commit', view.restrictions.edit)
158 self.assertEqual(['Restrict-Action-NeededPerm'], view.restrictions.other)
159 self.assertEqual('Restrict-View-Commit', view.labels[0].name)
160 self.assertTrue(view.labels[0].is_restrict)
161
162
163class RestrictionsViewTest(unittest.TestCase):
164 pass # TODO(jrobbins): write tests
165
166
167class AttachmentViewTest(unittest.TestCase):
168
169 def setUp(self):
170 self.orig_sign_attachment_id = attachment_helpers.SignAttachmentID
171 attachment_helpers.SignAttachmentID = (
172 lambda aid: 'signed_%d' % aid)
173
174 def tearDown(self):
175 attachment_helpers.SignAttachmentID = self.orig_sign_attachment_id
176
177 def MakeViewAndVerifyFields(
178 self, size, name, mimetype, expected_size_str, expect_viewable):
179 attach_pb = tracker_pb2.Attachment()
180 attach_pb.filesize = size
181 attach_pb.attachment_id = 12345
182 attach_pb.filename = name
183 attach_pb.mimetype = mimetype
184
185 view = tracker_views.AttachmentView(attach_pb, 'proj')
186 self.assertEqual('/images/paperclip.png', view.iconurl)
187 self.assertEqual(expected_size_str, view.filesizestr)
188 dl = 'attachment?aid=12345&signed_aid=signed_12345'
189 self.assertEqual(dl, view.downloadurl)
190 if expect_viewable:
191 self.assertEqual(dl + '&inline=1', view.url)
192 self.assertEqual(dl + '&inline=1&thumb=1', view.thumbnail_url)
193 else:
194 self.assertEqual(None, view.url)
195 self.assertEqual(None, view.thumbnail_url)
196
197 def testNonImage(self):
198 self.MakeViewAndVerifyFields(
199 123, 'file.ext', 'funky/bits', '123 bytes', False)
200
201 def testViewableImage(self):
202 self.MakeViewAndVerifyFields(
203 123, 'logo.gif', 'image/gif', '123 bytes', True)
204
205 self.MakeViewAndVerifyFields(
206 123, 'screenshot.jpg', 'image/jpeg', '123 bytes', True)
207
208 def testHugeImage(self):
209 self.MakeViewAndVerifyFields(
210 18 * 1024 * 1024, 'panorama.png', 'image/jpeg', '18.0 MB', False)
211
212 def testViewableText(self):
213 name = 'hello.c'
214 attach_pb = tracker_pb2.Attachment()
215 attach_pb.filesize = 1234
216 attach_pb.attachment_id = 12345
217 attach_pb.filename = name
218 attach_pb.mimetype = 'text/plain'
219 view = tracker_views.AttachmentView(attach_pb, 'proj')
220
221 view_url = '/p/proj/issues/attachmentText?aid=12345'
222 self.assertEqual(view_url, view.url)
223
224
225class LogoViewTest(unittest.TestCase):
226
227 def setUp(self):
228 self.mox = mox.Mox()
229
230 def tearDown(self):
231 self.mox.UnsetStubs()
232 self.mox.ResetAll()
233
234 def testProjectWithLogo(self):
235 bucket_name = 'testbucket'
236 logo_gcs_id = '123'
237 logo_file_name = 'logo.png'
238 project_pb = project_pb2.MakeProject(
239 'testProject', logo_gcs_id=logo_gcs_id, logo_file_name=logo_file_name)
240
241 self.mox.StubOutWithMock(app_identity, 'get_default_gcs_bucket_name')
242 app_identity.get_default_gcs_bucket_name().AndReturn(bucket_name)
243
244 self.mox.StubOutWithMock(gcs_helpers, 'SignUrl')
245 gcs_helpers.SignUrl(bucket_name,
246 logo_gcs_id + '-thumbnail').AndReturn('signed/url')
247 gcs_helpers.SignUrl(bucket_name, logo_gcs_id).AndReturn('signed/url')
248
249 self.mox.ReplayAll()
250
251 view = tracker_views.LogoView(project_pb)
252 self.mox.VerifyAll()
253 self.assertEqual('logo.png', view.filename)
254 self.assertEqual('image/png', view.mimetype)
255 self.assertEqual('signed/url', view.thumbnail_url)
256 self.assertEqual(
257 'signed/url&response-content-displacement=attachment%3B'
258 '+filename%3Dlogo.png', view.viewurl)
259
260 def testProjectWithNoLogo(self):
261 project_pb = project_pb2.MakeProject('testProject')
262 view = tracker_views.LogoView(project_pb)
263 self.assertEqual('', view.thumbnail_url)
264 self.assertEqual('', view.viewurl)
265
266
267class AmendmentViewTest(unittest.TestCase):
268 pass # TODO(jrobbins): write tests
269
270
271class ComponentDefViewTest(unittest.TestCase):
272 def setUp(self):
273 self.services = service_manager.Services(
274 user=fake.UserService(),
275 config=fake.ConfigService())
276 self.services.user.TestAddUser('admin@example.com', 111)
277 self.services.user.TestAddUser('cc@example.com', 222)
278 self.users_by_id = framework_views.MakeAllUserViews(
279 'cnxn', self.services.user, [111, 222])
280 self.services.config.TestAddLabelsDict({'Hot': 1, 'Cold': 2})
281 self.cd = tracker_bizobj.MakeComponentDef(
282 10, 789, 'UI', 'User interface', False,
283 [111], [222], 0, 111, label_ids=[1, 2])
284
285 def testRootComponent(self):
286 view = tracker_views.ComponentDefView(
287 'cnxn', self.services, self.cd, self.users_by_id)
288 self.assertEqual('', view.parent_path)
289 self.assertEqual('UI', view.leaf_name)
290 self.assertEqual('User interface', view.docstring_short)
291 self.assertEqual('admin@example.com', view.admins[0].email)
292 self.assertEqual(['Hot', 'Cold'], view.labels)
293 self.assertEqual('all toplevel active ', view.classes)
294
295 def testNestedComponent(self):
296 self.cd.path = 'UI>Dialogs>Print'
297 view = tracker_views.ComponentDefView(
298 'cnxn', self.services, self.cd, self.users_by_id)
299 self.assertEqual('UI>Dialogs', view.parent_path)
300 self.assertEqual('Print', view.leaf_name)
301 self.assertEqual('User interface', view.docstring_short)
302 self.assertEqual('admin@example.com', view.admins[0].email)
303 self.assertEqual(['Hot', 'Cold'], view.labels)
304 self.assertEqual('all active ', view.classes)
305
306
307class ComponentValueTest(unittest.TestCase):
308 pass # TODO(jrobbins): write tests
309
310
311class FieldValueViewTest(unittest.TestCase):
312
313 def setUp(self):
314 self.config = tracker_pb2.ProjectIssueConfig()
315 self.estdays_fd = tracker_bizobj.MakeFieldDef(
316 1, 789, 'EstDays', tracker_pb2.FieldTypes.INT_TYPE, None,
317 None, False, False, False, 3, 99, None, False, None, None,
318 None, 'no_action', 'descriptive docstring', False, approval_id=None,
319 is_phase_field=False)
320 self.designdoc_fd = tracker_bizobj.MakeFieldDef(
321 2, 789, 'DesignDoc', tracker_pb2.FieldTypes.STR_TYPE, 'Enhancement',
322 None, False, False, False, None, None, None, False, None, None,
323 None, 'no_action', 'descriptive docstring', False, approval_id=None,
324 is_phase_field=False)
325 self.mtarget_fd = tracker_bizobj.MakeFieldDef(
326 3, 789, 'M-Target', tracker_pb2.FieldTypes.INT_TYPE, 'Enhancement',
327 None, False, False, False, None, None, None, False, None, None,
328 None, 'no_action', 'doc doc', False, approval_id=None,
329 is_phase_field=True)
330 self.config.field_defs = [self.estdays_fd, self.designdoc_fd]
331
332 def testNoValues(self):
333 """We can create a FieldValueView with no values."""
334 values = []
335 derived_values = []
336 estdays_fvv = tracker_views.FieldValueView(
337 self.estdays_fd, self.config, values, derived_values, ['defect'],
338 phase_name='Gate')
339 self.assertEqual('EstDays', estdays_fvv.field_def.field_name)
340 self.assertEqual(3, estdays_fvv.field_def.min_value)
341 self.assertEqual(99, estdays_fvv.field_def.max_value)
342 self.assertEqual([], estdays_fvv.values)
343 self.assertEqual([], estdays_fvv.derived_values)
344
345 def testSomeValues(self):
346 """We can create a FieldValueView with some values."""
347 values = [template_helpers.EZTItem(val=12, docstring=None, idx=0)]
348 derived_values = [template_helpers.EZTItem(val=88, docstring=None, idx=0)]
349 estdays_fvv = tracker_views.FieldValueView(
350 self.estdays_fd, self.config, values, derived_values, ['defect'])
351 self.assertEqual(self.estdays_fd, estdays_fvv.field_def.field_def)
352 self.assertTrue(estdays_fvv.is_editable)
353 self.assertEqual(values, estdays_fvv.values)
354 self.assertEqual(derived_values, estdays_fvv.derived_values)
355 self.assertEqual('', estdays_fvv.phase_name)
356 self.assertEqual(ezt.boolean(False), estdays_fvv.field_def.is_phase_field)
357
358 def testApplicability(self):
359 """We know whether a field should show an editing widget."""
360 # Not the right type and has no values.
361 designdoc_fvv = tracker_views.FieldValueView(
362 self.designdoc_fd, self.config, [], [], ['defect'])
363 self.assertFalse(designdoc_fvv.applicable)
364 self.assertEqual('', designdoc_fvv.phase_name)
365 self.assertEqual(ezt.boolean(False), designdoc_fvv.field_def.is_phase_field)
366
367 # Has a value.
368 designdoc_fvv = tracker_views.FieldValueView(
369 self.designdoc_fd, self.config, ['fake value item'], [], ['defect'])
370 self.assertTrue(designdoc_fvv.applicable)
371
372 # Derived values don't cause editing fields to display.
373 designdoc_fvv = tracker_views.FieldValueView(
374 self.designdoc_fd, self.config, [], ['fake value item'], ['defect'])
375 self.assertFalse(designdoc_fvv.applicable)
376
377 # Applicable to this type of issue.
378 designdoc_fvv = tracker_views.FieldValueView(
379 self.designdoc_fd, self.config, [], [], ['enhancement'])
380 self.assertTrue(designdoc_fvv.applicable)
381
382 # Applicable to some issues in a bulk edit.
383 designdoc_fvv = tracker_views.FieldValueView(
384 self.designdoc_fd, self.config, [], [],
385 ['defect', 'task', 'enhancement'])
386 self.assertTrue(designdoc_fvv.applicable)
387
388 # Applicable to all issues.
389 estdays_fvv = tracker_views.FieldValueView(
390 self.estdays_fd, self.config, [], [], ['enhancement'])
391 self.assertTrue(estdays_fvv.applicable)
392
393 # Explicitly set to be applicable when showing bounce values.
394 designdoc_fvv = tracker_views.FieldValueView(
395 self.designdoc_fd, self.config, [], [], ['defect'],
396 applicable=True)
397 self.assertTrue(designdoc_fvv.applicable)
398
399 def testDisplay(self):
400 """We know when a value (or --) should be shown in the metadata column."""
401 # Not the right type and has no values.
402 designdoc_fvv = tracker_views.FieldValueView(
403 self.designdoc_fd, self.config, [], [], ['defect'])
404 self.assertFalse(designdoc_fvv.display)
405
406 # Has a value.
407 designdoc_fvv = tracker_views.FieldValueView(
408 self.designdoc_fd, self.config, ['fake value item'], [], ['defect'])
409 self.assertTrue(designdoc_fvv.display)
410
411 # Has a derived value.
412 designdoc_fvv = tracker_views.FieldValueView(
413 self.designdoc_fd, self.config, [], ['fake value item'], ['defect'])
414 self.assertTrue(designdoc_fvv.display)
415
416 # Applicable to this type of issue, it will show "--".
417 designdoc_fvv = tracker_views.FieldValueView(
418 self.designdoc_fd, self.config, [], [], ['enhancement'])
419 self.assertTrue(designdoc_fvv.display)
420
421 # Applicable to all issues, it will show "--".
422 estdays_fvv = tracker_views.FieldValueView(
423 self.estdays_fd, self.config, [], [], ['enhancement'])
424 self.assertTrue(estdays_fvv.display)
425
426 def testPhaseField(self):
427 mtarget_fvv = tracker_views.FieldValueView(
428 self.mtarget_fd, self.config, [], [], [], phase_name='Stage')
429 self.assertEqual('Stage', mtarget_fvv.phase_name)
430 self.assertEqual(ezt.boolean(True), mtarget_fvv.field_def.is_phase_field)
431
432
433class FVVFunctionsTest(unittest.TestCase):
434
435 def setUp(self):
436 self.config = tracker_pb2.ProjectIssueConfig()
437 self.estdays_fd = tracker_bizobj.MakeFieldDef(
438 1, 789, 'EstDays', tracker_pb2.FieldTypes.INT_TYPE, None,
439 None, False, False, False, 3, 99, None, False, None, None,
440 None, 'no_action', 'descriptive docstring', False, None, False)
441 self.os_fd = tracker_bizobj.MakeFieldDef(
442 2, 789, 'OS', tracker_pb2.FieldTypes.ENUM_TYPE,
443 'Enhancement', None, False, False, False, None, None, None,
444 False, None, None, None, 'no_action', 'descriptive docstring',
445 False, None, False)
446 self.milestone_fd = tracker_bizobj.MakeFieldDef(
447 3, 789, 'Launch-Milestone', tracker_pb2.FieldTypes.ENUM_TYPE,
448 'Enhancement', None, False, False, False, None, None, None,
449 False, None, None, None, 'no_action', 'descriptive docstring',
450 False, None, False)
451 self.config.field_defs = [self.estdays_fd, self.os_fd, self.milestone_fd]
452 self.config.well_known_labels = [
453 tracker_pb2.LabelDef(
454 label='Priority-High', label_docstring='Must be resolved'),
455 tracker_pb2.LabelDef(
456 label='Priority-Low', label_docstring='Can be slipped'),
457 ]
458
459 def testPrecomputeInfoForValueViews_NoValues(self):
460 """We can precompute info needed for an issue with no fields or labels."""
461 labels = []
462 derived_labels = []
463 field_values = []
464 phases = []
465 precomp_view_info = tracker_views._PrecomputeInfoForValueViews(
466 labels, derived_labels, field_values, self.config, phases)
467 (labels_by_prefix, der_labels_by_prefix, field_values_by_id,
468 label_docs, phases_by_name) = precomp_view_info
469 self.assertEqual({}, labels_by_prefix)
470 self.assertEqual({}, der_labels_by_prefix)
471 self.assertEqual({}, field_values_by_id)
472 self.assertEqual(
473 {'priority-high': 'Must be resolved',
474 'priority-low': 'Can be slipped'},
475 label_docs)
476 self.assertEqual({}, phases_by_name)
477
478 def testPrecomputeInfoForValueViews_SomeValues(self):
479 """We can precompute info needed for an issue with fields and labels."""
480 labels = ['Priority-Low', 'GoodFirstBug', 'Feature-UI', 'Feature-Installer',
481 'Launch-Milestone-66']
482 derived_labels = ['OS-Windows', 'OS-Linux']
483 field_values = [
484 tracker_bizobj.MakeFieldValue(1, 5, None, None, None, None, False),
485 ]
486 phase_1 = tracker_pb2.Phase(phase_id=1, name='Stable')
487 phase_2 = tracker_pb2.Phase(phase_id=2, name='Beta')
488 phase_3 = tracker_pb2.Phase(phase_id=3, name='stable')
489 precomp_view_info = tracker_views._PrecomputeInfoForValueViews(
490 labels, derived_labels, field_values, self.config,
491 phases=[phase_1, phase_2, phase_3])
492 (labels_by_prefix, der_labels_by_prefix, field_values_by_id,
493 _label_docs, phases_by_name) = precomp_view_info
494 self.assertEqual(
495 {'priority': ['Low'],
496 'feature': ['UI', 'Installer'],
497 'launch-milestone': ['66']},
498 labels_by_prefix)
499 self.assertEqual(
500 {'os': ['Windows', 'Linux']},
501 der_labels_by_prefix)
502 self.assertEqual(
503 {1: field_values},
504 field_values_by_id)
505 self.assertEqual(
506 {'stable': [phase_1, phase_3],
507 'beta': [phase_2]},
508 phases_by_name)
509
510 def testMakeAllFieldValueViews(self):
511 labels = ['Priority-Low', 'GoodFirstBug', 'Feature-UI', 'Feature-Installer',
512 'Launch-Milestone-66']
513 derived_labels = ['OS-Windows', 'OS-Linux']
514 self.config.field_defs.append(tracker_bizobj.MakeFieldDef(
515 4, 789, 'UIMocks', tracker_pb2.FieldTypes.URL_TYPE,
516 'Enhancement', None, False, False, False, None, None, None,
517 False, None, None, None, 'no_action', 'descriptive docstring',
518 False, approval_id=23, is_phase_field=False))
519 self.config.field_defs.append(tracker_bizobj.MakeFieldDef(
520 5, 789, 'LegalFAQs', tracker_pb2.FieldTypes.URL_TYPE,
521 'Enhancement', None, False, False, False, None, None, None,
522 False, None, None, None, 'no_action', 'descriptive docstring',
523 False, approval_id=26, is_phase_field=False))
524 self.config.field_defs.append(tracker_bizobj.MakeFieldDef(
525 23, 789, 'Legal', tracker_pb2.FieldTypes.APPROVAL_TYPE,
526 'Enhancement', None, False, False, False, None, None, None,
527 False, None, None, None, 'no_action', 'descriptive docstring',
528 False, approval_id=None, is_phase_field=False))
529 self.config.field_defs.append(tracker_bizobj.MakeFieldDef(
530 26, 789, 'UI', tracker_pb2.FieldTypes.APPROVAL_TYPE,
531 'Enhancement', None, False, False, False, None, None, None,
532 False, None, None, None, 'no_action', 'descriptive docstring',
533 False, approval_id=None, is_phase_field=False))
534 self.config.field_defs.append(tracker_bizobj.MakeFieldDef(
535 27, 789, 'M-Target', tracker_pb2.FieldTypes.INT_TYPE,
536 'Enhancement', None, False, False, False, None, None, None,
537 False, None, None, None, 'no_action', 'descriptive docstring',
538 False, approval_id=None, is_phase_field=True))
539 field_values = [
540 tracker_bizobj.MakeFieldValue(1, 5, None, None, None, None, False),
541 tracker_bizobj.MakeFieldValue(
542 27, 74, None, None, None, None, False, phase_id=3),
543 # phase_id=4 does not belong to any of the phases given below.
544 # this field value should not show up in the views.
545 tracker_bizobj.MakeFieldValue(
546 27, 79, None, None, None, None, False, phase_id=4),
547 ]
548 users_by_id = {}
549 phase_1 = tracker_pb2.Phase(phase_id=1, name='Stable')
550 phase_2 = tracker_pb2.Phase(phase_id=2, name='Beta')
551 phase_3 = tracker_pb2.Phase(phase_id=3, name='stable')
552 fvvs = tracker_views.MakeAllFieldValueViews(
553 self.config, labels, derived_labels, field_values, users_by_id,
554 parent_approval_ids=[23], phases=[phase_1, phase_2, phase_3])
555 self.assertEqual(9, len(fvvs))
556 # Values are sorted by (applicable_type, field_name).
557 logging.info([fv.field_name for fv in fvvs])
558 (estdays_fvv, launch_milestone_fvv, legal_fvv, legal_faq_fvv,
559 beta_mtarget_fvv, stable_mtarget_fvv, os_fvv, ui_fvv, ui_mocks_fvv) = fvvs
560 self.assertEqual('EstDays', estdays_fvv.field_name)
561 self.assertEqual(1, len(estdays_fvv.values))
562 self.assertEqual(0, len(estdays_fvv.derived_values))
563 self.assertEqual('Launch-Milestone', launch_milestone_fvv.field_name)
564 self.assertEqual(1, len(launch_milestone_fvv.values))
565 self.assertEqual(0, len(launch_milestone_fvv.derived_values))
566 self.assertEqual('OS', os_fvv.field_name)
567 self.assertEqual(0, len(os_fvv.values))
568 self.assertEqual(2, len(os_fvv.derived_values))
569 self.assertEqual(ui_mocks_fvv.field_name, 'UIMocks')
570 self.assertEqual(ui_mocks_fvv.phase_name, '')
571 self.assertTrue(ui_mocks_fvv.applicable)
572 self.assertEqual(legal_faq_fvv.field_name, 'LegalFAQs')
573 self.assertFalse(legal_faq_fvv.applicable)
574 self.assertFalse(legal_fvv.applicable)
575 self.assertFalse(ui_fvv.applicable)
576 self.assertEqual('M-Target', stable_mtarget_fvv.field_name)
577 self.assertEqual('stable', stable_mtarget_fvv.phase_name)
578 self.assertEqual(1, len(stable_mtarget_fvv.values))
579 self.assertEqual(74, stable_mtarget_fvv.values[0].val)
580 self.assertEqual(0, len(stable_mtarget_fvv.derived_values))
581 self.assertEqual('M-Target', beta_mtarget_fvv.field_name)
582 self.assertEqual('beta', beta_mtarget_fvv.phase_name)
583 self.assertEqual(0, len(beta_mtarget_fvv.values))
584 self.assertEqual(0, len(beta_mtarget_fvv.values))
585
586 def testMakeFieldValueView(self):
587 pass # Covered by testMakeAllFieldValueViews()
588
589 def testMakeFieldValueItemsTest(self):
590 pass # Covered by testMakeAllFieldValueViews()
591
592 def testMakeBounceFieldValueViews(self):
593 config = tracker_pb2.ProjectIssueConfig()
594 fd = tracker_pb2.FieldDef(
595 field_id=3, field_type=tracker_pb2.FieldTypes.INT_TYPE,
596 applicable_type='', field_name='EstDays')
597 phase_fd = tracker_pb2.FieldDef(
598 field_id=4, field_type=tracker_pb2.FieldTypes.INT_TYPE,
599 applicable_type='', field_name='Gump')
600 config.field_defs = [fd,
601 phase_fd,
602 tracker_pb2.FieldDef(
603 field_id=5, field_type=tracker_pb2.FieldTypes.STR_TYPE)
604 ]
605 parsed_fvs = {3: [455]}
606 parsed_phase_fvs = {
607 4: {'stable': [73, 74], 'beta': [8], 'beta-exp': [75]},
608 }
609 fvs = tracker_views.MakeBounceFieldValueViews(
610 parsed_fvs, parsed_phase_fvs, config)
611
612 self.assertEqual(len(fvs), 4)
613
614 estdays_ezt_fv = template_helpers.EZTItem(val=455, docstring='', idx=0)
615 expected = tracker_views.FieldValueView(
616 fd, config, [estdays_ezt_fv], [], [])
617 self.assertEqual(fvs[0].field_name, expected.field_name)
618 self.assertEqual(fvs[0].values[0].val, expected.values[0].val)
619 self.assertEqual(fvs[0].values[0].idx, expected.values[0].idx)
620 self.assertTrue(fvs[0].applicable)
621
622 self.assertEqual(fvs[1].field_name, phase_fd.field_name)
623 self.assertEqual(fvs[2].field_name, phase_fd.field_name)
624 self.assertEqual(fvs[3].field_name, phase_fd.field_name)
625
626 fd.approval_id = 23
627 config.field_defs = [fd,
628 tracker_pb2.FieldDef(
629 field_id=23, field_name='Legal',
630 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE)]
631 fvs = tracker_views.MakeBounceFieldValueViews(parsed_fvs, {}, config)
632 self.assertTrue(fvs[0].applicable)
633
634
635class ConvertLabelsToFieldValuesTest(unittest.TestCase):
636
637 def testConvertLabelsToFieldValues_NoLabels(self):
638 result = tracker_views._ConvertLabelsToFieldValues(
639 [], 'opsys', {})
640 self.assertEqual([], result)
641
642 def testConvertLabelsToFieldValues_NoMatch(self):
643 result = tracker_views._ConvertLabelsToFieldValues(
644 [], 'opsys', {})
645 self.assertEqual([], result)
646
647 def testConvertLabelsToFieldValues_HasMatch(self):
648 result = tracker_views._ConvertLabelsToFieldValues(
649 ['OSX'], 'opsys', {})
650 self.assertEqual(1, len(result))
651 self.assertEqual('OSX', result[0].val)
652 self.assertEqual('', result[0].docstring)
653
654 result = tracker_views._ConvertLabelsToFieldValues(
655 ['OSX', 'All'], 'opsys', {'opsys-all': 'Happens everywhere'})
656 self.assertEqual(2, len(result))
657 self.assertEqual('OSX', result[0].val)
658 self.assertEqual('', result[0].docstring)
659 self.assertEqual('All', result[1].val)
660 self.assertEqual('Happens everywhere', result[1].docstring)
661
662
663class FieldDefViewTest(unittest.TestCase):
664
665 def setUp(self):
666 self.approval_fd = tracker_bizobj.MakeFieldDef(
667 1, 789, 'LaunchApproval', tracker_pb2.FieldTypes.APPROVAL_TYPE, None,
668 None, True, True, False, 3, 99, None, False, None, None,
669 None, 'no_action', 'descriptive docstring', False, None, False)
670
671 self.approval_def = tracker_pb2.ApprovalDef(
672 approval_id=1, approver_ids=[111], survey='question?')
673
674 self.field_def = tracker_bizobj.MakeFieldDef(
675 2, 789, 'AffectedUsers', tracker_pb2.FieldTypes.INT_TYPE, None,
676 None, True, True, False, 3, 99, None, False, None, None,
677 None, 'no_action', 'descriptive docstring', False, 1, False)
678
679 self.field_def.admin_ids = [222]
680 self.field_def.editor_ids = [111, 333]
681
682 def testFieldDefView_Normal(self):
683 config = _MakeConfig()
684 config.field_defs.append(self.approval_fd)
685 config.approval_defs.append(self.approval_def)
686
687 user_view_1 = framework_views.StuffUserView(111, 'uv1@example.com', False)
688 user_view_2 = framework_views.StuffUserView(222, 'uv2@example.com', False)
689 user_view_3 = framework_views.StuffUserView(333, 'uv3@example.com', False)
690 user_views = {111: user_view_1, 222: user_view_2, 333: user_view_3}
691 view = tracker_views.FieldDefView(
692 self.field_def, config, user_views=user_views)
693
694 self.assertEqual('AffectedUsers', view.field_name)
695 self.assertEqual(self.field_def, view.field_def)
696 self.assertEqual('descriptive docstring', view.docstring_short)
697 self.assertEqual('INT_TYPE', view.type_name)
698 self.assertEqual([], view.choices)
699 self.assertEqual('required', view.importance)
700 self.assertEqual(3, view.min_value)
701 self.assertEqual(99, view.max_value)
702 self.assertEqual('no_action', view.date_action_str)
703 self.assertEqual(view.approval_id, 1)
704 self.assertEqual(view.is_approval_subfield, ezt.boolean(True))
705 self.assertEqual(view.approvers, [])
706 self.assertEqual(view.survey, '')
707 self.assertEqual(view.survey_questions, [])
708 self.assertEqual(len(view.admins), 1)
709 self.assertEqual(len(view.editors), 2)
710 self.assertIsNone(view.is_phase_field)
711 self.assertIsNone(view.is_restricted_field)
712
713 def testFieldDefView_Approval(self):
714 config = _MakeConfig()
715 approver_view = framework_views.StuffUserView(
716 111, 'shouldnotmatter@ch.org', False)
717 user_views = {111: approver_view}
718
719 view = tracker_views.FieldDefView(
720 self.approval_fd, config,
721 user_views= user_views, approval_def=self.approval_def)
722 self.assertEqual(view.approvers, [approver_view])
723 self.assertEqual(view.survey, self.approval_def.survey)
724 self.assertEqual(view.survey_questions, [view.survey])
725
726 self.approval_def.survey = None
727 view = tracker_views.FieldDefView(
728 self.approval_fd, config,
729 user_views= user_views, approval_def=self.approval_def)
730 self.assertEqual(view.survey, '')
731 self.assertEqual(view.survey_questions, [])
732
733 self.approval_def.survey = 'Q1\nQ2\nQ3'
734 view = tracker_views.FieldDefView(
735 self.approval_fd, config,
736 user_views= user_views, approval_def=self.approval_def)
737 self.assertEqual(view.survey, self.approval_def.survey)
738 self.assertEqual(view.survey_questions, ['Q1', 'Q2', 'Q3'])
739
740
741class IssueTemplateViewTest(unittest.TestCase):
742 pass # TODO(jrobbins): write tests
743
744
745class MakeFieldUserViewsTest(unittest.TestCase):
746 pass # TODO(jrobbins): write tests
747
748
749class ConfigViewTest(unittest.TestCase):
750 pass # TODO(jrobbins): write tests
751
752
753class ConfigFunctionsTest(unittest.TestCase):
754
755 def setUp(self):
756 self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(768)
757
758 def testStatusDefsAsText(self):
759 open_text, closed_text = tracker_views.StatusDefsAsText(self.config)
760
761 for wks in tracker_constants.DEFAULT_WELL_KNOWN_STATUSES:
762 status, doc, means_open, _deprecated = wks
763 if means_open:
764 self.assertIn(status, open_text)
765 self.assertIn(doc, open_text)
766 else:
767 self.assertIn(status, closed_text)
768 self.assertIn(doc, closed_text)
769
770 self.assertEqual(
771 len(tracker_constants.DEFAULT_WELL_KNOWN_STATUSES),
772 len(open_text.split('\n')) + len(closed_text.split('\n')))
773
774 def testLabelDefsAsText(self):
775 # Note: Day-Monday will not be part of the result because it is masked.
776 self.config.field_defs.append(tracker_pb2.FieldDef(
777 field_id=1, field_name='Day',
778 field_type=tracker_pb2.FieldTypes.ENUM_TYPE))
779 self.config.well_known_labels.append(tracker_pb2.LabelDef(
780 label='Day-Monday'))
781 labels_text = tracker_views.LabelDefsAsText(self.config)
782
783 for wkl in tracker_constants.DEFAULT_WELL_KNOWN_LABELS:
784 label, doc, _deprecated = wkl
785 self.assertIn(label, labels_text)
786 self.assertIn(doc, labels_text)
787 self.assertEqual(
788 len(tracker_constants.DEFAULT_WELL_KNOWN_LABELS),
789 len(labels_text.split('\n')))