617
626
self.assertIn(series.product.displayname, content)
629
class TestBugTaskDeleteLinks(TestCaseWithFactory):
630
""" Test that the delete icons/links are correctly rendered.
632
Bug task deletion is protected by a feature flag.
635
layer = DatabaseFunctionalLayer
637
def test_cannot_delete_only_bugtask(self):
638
# The last bugtask cannot be deleted.
639
bug = self.factory.makeBug()
640
login_person(bug.owner)
641
view = create_initialized_view(
642
bug, name='+bugtasks-and-nominations-table')
643
row_view = view._getTableRowView(bug.default_bugtask, False, False)
644
self.assertFalse(row_view.user_can_delete_bugtask)
645
del get_property_cache(row_view).user_can_delete_bugtask
646
with FeatureFixture(DELETE_BUGTASK_ENABLED):
647
self.assertFalse(row_view.user_can_delete_bugtask)
649
def test_can_delete_bugtask_if_authorised(self):
650
# The bugtask can be deleted if the user if authorised.
651
bug = self.factory.makeBug()
652
bugtask = self.factory.makeBugTask(bug=bug)
653
login_person(bugtask.owner)
654
view = create_initialized_view(
655
bug, name='+bugtasks-and-nominations-table',
656
principal=bugtask.owner)
657
row_view = view._getTableRowView(bugtask, False, False)
658
self.assertFalse(row_view.user_can_delete_bugtask)
659
del get_property_cache(row_view).user_can_delete_bugtask
661
with FeatureFixture(DELETE_BUGTASK_ENABLED):
662
self.assertTrue(row_view.user_can_delete_bugtask)
664
def test_bugtask_delete_icon(self):
665
# The bugtask delete icon is rendered correctly for those tasks the
666
# user is allowed to delete.
667
bug = self.factory.makeBug()
668
bugtask_owner = self.factory.makePerson()
669
bugtask = self.factory.makeBugTask(bug=bug, owner=bugtask_owner)
670
with FeatureFixture(DELETE_BUGTASK_ENABLED):
671
login_person(bugtask.owner)
672
getUtility(ILaunchBag).add(bug.default_bugtask)
673
view = create_initialized_view(
674
bug, name='+bugtasks-and-nominations-table',
675
principal=bugtask.owner)
676
# We render the bug task table rows - there are 2 bug tasks.
677
subviews = view.getBugTaskAndNominationViews()
678
self.assertEqual(2, len(subviews))
679
default_bugtask_contents = subviews[0]()
680
bugtask_contents = subviews[1]()
681
# bugtask can be deleted because the user owns it.
682
delete_icon = find_tag_by_id(
683
bugtask_contents, 'bugtask-delete-task%d' % bugtask.id)
684
delete_url = canonical_url(
685
bugtask, rootsite='bugs', view_name='+delete')
686
self.assertEqual(delete_url, delete_icon['href'])
687
# default_bugtask cannot be deleted.
688
delete_icon = find_tag_by_id(
689
default_bugtask_contents,
690
'bugtask-delete-task%d' % bug.default_bugtask.id)
691
self.assertIsNone(delete_icon)
693
def test_client_cache_contents(self):
694
""" Test that the client cache contains the expected data.
696
The cache data is used by the Javascript to enable the delete
697
links to work as expected.
699
bug = self.factory.makeBug()
700
bugtask_owner = self.factory.makePerson()
701
bugtask = self.factory.makeBugTask(bug=bug, owner=bugtask_owner)
702
with FeatureFixture(DELETE_BUGTASK_ENABLED):
703
login_person(bugtask.owner)
704
getUtility(ILaunchBag).add(bug.default_bugtask)
705
view = create_initialized_view(
706
bug, name='+bugtasks-and-nominations-table',
707
principal=bugtask.owner)
709
cache = IJSONRequestCache(view.request)
710
all_bugtask_data = cache.objects['bugtask_data']
712
def check_bugtask_data(bugtask, can_delete):
713
self.assertIn(bugtask.id, all_bugtask_data)
714
bugtask_data = all_bugtask_data[bugtask.id]
716
'task%d' % bugtask.id, bugtask_data['form_row_id'])
718
'tasksummary%d' % bugtask.id, bugtask_data['row_id'])
719
self.assertEqual(can_delete, bugtask_data['user_can_delete'])
721
check_bugtask_data(bug.default_bugtask, False)
722
check_bugtask_data(bugtask, True)
725
class TestBugTaskDeleteView(TestCaseWithFactory):
726
"""Test the bug task delete form."""
728
layer = DatabaseFunctionalLayer
730
def test_delete_view_rendering(self):
731
# Test the view rendering, including confirmation message, cancel url.
732
bug = self.factory.makeBug()
733
bugtask = self.factory.makeBugTask(bug=bug)
734
bug_url = canonical_url(bugtask.bug, rootsite='bugs')
735
# Set up request so that the ReturnToReferrerMixin can correctly
736
# extra the referer url.
737
server_url = canonical_url(
738
getUtility(ILaunchpadRoot), rootsite='bugs')
739
extra = {'HTTP_REFERER': bug_url}
740
with FeatureFixture(DELETE_BUGTASK_ENABLED):
741
login_person(bugtask.owner)
742
view = create_initialized_view(
743
bugtask, name='+delete', principal=bugtask.owner,
744
server_url=server_url, **extra)
745
contents = view.render()
746
confirmation_message = find_tag_by_id(
747
contents, 'confirmation-message')
748
self.assertIsNotNone(confirmation_message)
749
self.assertEqual(bug_url, view.cancel_url)
751
def test_delete_action(self):
752
# Test that the delete action works as expected.
753
bug = self.factory.makeBug()
754
bugtask = self.factory.makeBugTask(bug=bug)
755
target_name = bugtask.bugtargetdisplayname
756
with FeatureFixture(DELETE_BUGTASK_ENABLED):
757
login_person(bugtask.owner)
759
'field.actions.delete_bugtask': 'Delete',
761
view = create_initialized_view(
762
bugtask, name='+delete', form=form, principal=bugtask.owner)
763
self.assertEqual([bug.default_bugtask], bug.bugtasks)
764
notifications = view.request.response.notifications
765
self.assertEqual(1, len(notifications))
766
expected = 'This bug no longer affects %s.' % target_name
767
self.assertEqual(expected, notifications[0].message)
769
def _create_bugtask_to_delete(self):
770
bug = self.factory.makeBug()
771
bugtask = self.factory.makeBugTask(bug=bug)
772
target_name = bugtask.bugtargetdisplayname
773
bugtask_url = canonical_url(bugtask, rootsite='bugs')
774
return bug, bugtask, target_name, bugtask_url
776
def test_ajax_delete_current_bugtask(self):
777
# Test that deleting the current bugtask returns a JSON dict
778
# containing the URL of the bug's default task to redirect to.
779
bug, bugtask, target_name, bugtask_url = (
780
self._create_bugtask_to_delete())
781
with FeatureFixture(DELETE_BUGTASK_ENABLED):
782
login_person(bugtask.owner)
783
# Set up the request so that we correctly simulate an XHR call
784
# from the URL of the bugtask we are deleting.
785
server_url = canonical_url(
786
getUtility(ILaunchpadRoot), rootsite='bugs')
788
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
789
'HTTP_REFERER': bugtask_url,
792
'field.actions.delete_bugtask': 'Delete'
794
view = create_initialized_view(
795
bugtask, name='+delete', server_url=server_url, form=form,
796
principal=bugtask.owner, **extra)
797
result_data = simplejson.loads(view.render())
798
self.assertEqual([bug.default_bugtask], bug.bugtasks)
799
notifications = simplejson.loads(
800
view.request.response.getHeader('X-Lazr-Notifications'))
801
self.assertEqual(1, len(notifications))
802
expected = 'This bug no longer affects %s.' % target_name
803
self.assertEqual(expected, notifications[0][1])
806
view.request.response.getHeader('content-type'))
807
expected_url = canonical_url(bug.default_bugtask, rootsite='bugs')
808
self.assertEqual(dict(bugtask_url=expected_url), result_data)
810
def test_ajax_delete_non_current_bugtask(self):
811
# Test that deleting the non-current bugtask returns the new bugtasks
813
bug, bugtask, target_name, bugtask_url = (
814
self._create_bugtask_to_delete())
815
default_bugtask_url = canonical_url(
816
bug.default_bugtask, rootsite='bugs')
817
with FeatureFixture(DELETE_BUGTASK_ENABLED):
818
login_person(bugtask.owner)
819
# Set up the request so that we correctly simulate an XHR call
820
# from the URL of the default bugtask, not the one we are
822
server_url = canonical_url(
823
getUtility(ILaunchpadRoot), rootsite='bugs')
825
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
826
'HTTP_REFERER': default_bugtask_url,
829
'field.actions.delete_bugtask': 'Delete'
831
view = create_initialized_view(
832
bugtask, name='+delete', server_url=server_url, form=form,
833
principal=bugtask.owner, **extra)
834
result_html = view.render()
835
self.assertEqual([bug.default_bugtask], bug.bugtasks)
836
notifications = view.request.response.notifications
837
self.assertEqual(1, len(notifications))
838
expected = 'This bug no longer affects %s.' % target_name
839
self.assertEqual(expected, notifications[0].message)
841
view.request.response.getHeader('content-type'), 'text/html')
842
table = find_tag_by_id(result_html, 'affected-software')
843
self.assertIsNotNone(table)
844
[row] = table.tbody.findAll('tr', {'class': 'highlight'})
845
target_link = row.find('a', {'class': 'sprite product'})
847
bug.default_bugtask.bugtargetdisplayname, target_link)
620
850
class TestBugTasksAndNominationsViewAlsoAffects(TestCaseWithFactory):
621
851
""" Tests the boolean methods on the view used to indicate whether the
622
852
Also Affects... links should be allowed or not. Currently these