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