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