def test_rendering(self): self.assertThat( self.makeView("+new-webhook")(), soupmatchers.HTMLContains( soupmatchers.Within(breadcrumbs_tag, webhooks_collection_crumb_tag), soupmatchers.Within( breadcrumbs_tag, soupmatchers.Tag('add webhook breadcrumb', 'li', text=re.compile('Add webhook'))), soupmatchers.Tag('cancel link', 'a', text='Cancel', attrs={'href': re.compile(r'/\+webhooks$')})))
def test_rendering(self): self.assertThat( self.makeView("+index")(), soupmatchers.HTMLContains( soupmatchers.Within(breadcrumbs_tag, webhooks_collection_crumb_tag), soupmatchers.Within( breadcrumbs_tag, soupmatchers.Tag('webhook breadcrumb', 'li', text=re.compile( re.escape( self.webhook.delivery_url)))), soupmatchers.Tag('delete link', 'a', text='Delete webhook', attrs={'href': re.compile(r'/\+delete$')})))
def test_store_upload_status_failed_with_extended_error_message(self): build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT) job = getUtility(ISnapStoreUploadJobSource).create(build) naked_job = removeSecurityProxy(job) naked_job.job._status = JobStatus.FAILED naked_job.error_message = "This should not be shown." naked_job.error_messages = [ {"message": ( "The new version submitted for 'name' does not match the " "upload ('other-name').")}, {"message": "Scan failed.", "link": "link1"}, {"message": "Classic not allowed.", "link": "link2"}] build_view = create_initialized_view(build, "+index") built_view = build_view() self.assertThat(built_view, Not(soupmatchers.HTMLContains( soupmatchers.Tag( "store upload status", "li", attrs={"id": "store-upload-status"}, text=re.compile('.*This should not be shown.*'))))) self.assertThat(built_view, soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag( "store upload status", "li", attrs={"id": "store-upload-status"}), soupmatchers.Tag( "store upload error messages", "ul", attrs={"id": "store-upload-error-messages"})))) self.assertThat(built_view, soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag( "store upload error messages", "ul", attrs={"id": "store-upload-error-messages"}), soupmatchers.Tag( "store upload error message", "li", text=re.compile(".*The new version.*"))))) self.assertThat(built_view, soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag( "store upload error messages", "ul", attrs={"id": "store-upload-error-messages"}), soupmatchers.Within( soupmatchers.Tag( "store upload error message", "li", text=re.compile(".*Scan failed\..*")), soupmatchers.Tag( "store upload error link", "a", attrs={"href": "link1"}, text="(?)"))))) self.assertThat(built_view, soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag( "store upload error messages", "ul", attrs={"id": "store-upload-error-messages"}), soupmatchers.Within( soupmatchers.Tag( "store upload error message", "li", text=re.compile(".*Classic not allowed\..*")), soupmatchers.Tag( "store upload error link", "a", attrs={"href": "link2"}, text="(?)")))))
def test_store_upload_status_completed(self): build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT) job = getUtility(ISnapStoreUploadJobSource).create(build) naked_job = removeSecurityProxy(job) naked_job.job._status = JobStatus.COMPLETED naked_job.store_url = "http://sca.example/dev/click-apps/1/rev/1/" build_view = create_initialized_view(build, "+index") self.assertThat(build_view(), soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag( "store upload status", "li", attrs={"id": "store-upload-status"}), soupmatchers.Tag( "store link", "a", attrs={"href": job.store_url}, text=re.compile( r"^\s*Manage this package in the store\s*$")))))
def test_store_upload_status_release_failed(self): build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT) job = getUtility(ISnapStoreUploadJobSource).create(build) naked_job = removeSecurityProxy(job) naked_job.job._status = JobStatus.FAILED naked_job.store_url = "http://sca.example/dev/click-apps/1/rev/1/" naked_job.error_message = "Failed to publish" build_view = create_initialized_view(build, "+index") self.assertThat(build_view(), soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag( "store upload status", "li", attrs={"id": "store-upload-status"}, text=re.compile( r"^\s*Releasing package to channels failed:\s+" r"Failed to publish\s*$")), soupmatchers.Tag( "store link", "a", attrs={"href": job.store_url}))))
def test_store_upload_status_failed(self): build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT) job = getUtility(ISnapStoreUploadJobSource).create(build) naked_job = removeSecurityProxy(job) naked_job.job._status = JobStatus.FAILED naked_job.error_message = "Scan failed." build_view = create_initialized_view(build, "+index") self.assertThat(build_view(), soupmatchers.HTMLContains( soupmatchers.Tag( "store upload status", "li", attrs={"id": "store-upload-status"}), soupmatchers.Within( soupmatchers.Tag( "store upload error messages", "ul", attrs={"id": "store-upload-error-messages"}), soupmatchers.Tag( "store upload error message", "li", text=re.compile(r"^\s*Scan failed.\s*$")))))
class DistroSeriesDifferenceTemplateTestCase(TestCaseWithFactory): layer = LaunchpadFunctionalLayer def number_of_request_diff_texts(self, html_or_soup): """Returns the number of request diff text.""" if not (isinstance(html_or_soup, BeautifulSoup)): soup = BeautifulSoup(html_or_soup) else: soup = html_or_soup class_dict = {'class': re.compile('request-derived-diff')} return len(soup.findAll('span', class_dict)) def contains_one_link_to_diff(self, html_or_soup, package_diff): """Return whether the html contains a link to the diff content.""" if not (isinstance(html_or_soup, BeautifulSoup)): soup = BeautifulSoup(html_or_soup) else: soup = html_or_soup return 1 == len( soup.findAll('a', href=package_diff.diff_content.http_url)) def test_both_request_diff_texts_rendered(self): # An unlinked description of a potential diff is displayed when # no diff is present. ds_diff = self.factory.makeDistroSeriesDifference( set_base_version=True) with person_logged_in(self.factory.makePerson()): view = create_initialized_view( ds_diff, '+listing-distroseries-extra') # , principal=user) soup = BeautifulSoup(view()) # Both diffs present simple text repr. of proposed diff. self.assertEqual(2, self.number_of_request_diff_texts(soup)) def test_source_diff_rendering_diff(self): # A linked description of the diff is displayed when # it is present. ds_diff = self.factory.makeDistroSeriesDifference( set_base_version=True) with person_logged_in(self.factory.makePerson()): ds_diff.package_diff = self.factory.makePackageDiff() view = create_initialized_view(ds_diff, '+listing-distroseries-extra') html = view() # The text for the parent diff remains, but the source package # diff is now a link. self.assertEqual(1, self.number_of_request_diff_texts(html)) self.assertTrue( self.contains_one_link_to_diff(html, ds_diff.package_diff)) def test_source_diff_rendering_diff_no_link(self): # The status of the package is shown if the package diff is in a # PENDING or FAILED state. ds_diff = self.factory.makeDistroSeriesDifference( set_base_version=True) statuses_and_classes = [(PackageDiffStatus.PENDING, 'PENDING'), (PackageDiffStatus.FAILED, 'FAILED')] for status, css_class in statuses_and_classes: with person_logged_in(self.factory.makePerson()): ds_diff.package_diff = self.factory.makePackageDiff( status=status) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') soup = BeautifulSoup(view()) # Only one link since the other package diff is not COMPLETED. self.assertEqual(1, self.number_of_request_diff_texts(soup)) # The diff has a css_class class. self.assertEqual( 1, len(soup.findAll('span', {'class': re.compile(css_class)}))) def test_parent_source_diff_rendering_diff_no_link(self): # The status of the package is shown if the parent package diff is # in a PENDING or FAILED state. ds_diff = self.factory.makeDistroSeriesDifference( set_base_version=True) statuses_and_classes = [(PackageDiffStatus.PENDING, 'PENDING'), (PackageDiffStatus.FAILED, 'FAILED')] for status, css_class in statuses_and_classes: with person_logged_in(self.factory.makePerson()): ds_diff.parent_package_diff = self.factory.makePackageDiff( status=status) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') soup = BeautifulSoup(view()) # Only one link since the other package diff is not COMPLETED. self.assertEqual(1, self.number_of_request_diff_texts(soup)) # The diff has a css_class class. self.assertEqual( 1, len(soup.findAll('span', {'class': re.compile(css_class)}))) def test_source_diff_rendering_no_source(self): # If there is no source pub for this difference, then we don't # display even the request for a diff. missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES ds_diff = self.factory.makeDistroSeriesDifference( difference_type=missing_type) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') self.assertEqual(0, self.number_of_request_diff_texts(view())) def test_parent_source_diff_rendering_diff(self): # A linked description of the diff is displayed when # it is present. ds_diff = self.factory.makeDistroSeriesDifference( set_base_version=True) with person_logged_in(self.factory.makePerson()): ds_diff.parent_package_diff = self.factory.makePackageDiff() view = create_initialized_view(ds_diff, '+listing-distroseries-extra') # The text for the source diff remains, but the parent package # diff is now a link. html = view() self.assertEqual(1, self.number_of_request_diff_texts(html)) self.assertTrue( self.contains_one_link_to_diff(html, ds_diff.parent_package_diff)) def test_parent_source_diff_rendering_no_source(self): # If there is no source pub for this difference, then we don't # display even the request for a diff. unique_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES ds_diff = self.factory.makeDistroSeriesDifference( difference_type=unique_type) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') self.assertEqual(0, self.number_of_request_diff_texts(view())) def test_comments_rendered(self): # If there are comments on the difference, they are rendered. ds_diff = self.factory.makeDistroSeriesDifference() person = self.factory.makePerson() with person_logged_in(person): ds_diff.addComment(person, "I'm working on this.") ds_diff.addComment(person, "Here's another comment.") view = create_initialized_view(ds_diff, '+listing-distroseries-extra') soup = BeautifulSoup(view()) self.assertEqual( 1, len(soup.findAll('pre', text="I'm working on this."))) self.assertEqual( 1, len(soup.findAll('pre', text="Here's another comment."))) def test_last_common_version_is_linked(self): # The "Last Common Version" version text should link to the # parent distro sourcepackagerelease page. ds_diff = removeSecurityProxy( self.factory.makeDistroSeriesDifference(set_base_version=True)) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') page = view() distro = ds_diff.parent_series.distribution sourcepackagerelease = ds_diff.parent_source_package_release url = canonical_url(DistributionSourcePackageRelease( distro, sourcepackagerelease), force_local_path=True) anchor_matcher = soupmatchers.HTMLContains( soupmatchers.Tag("VERSION LINK", 'a', attrs=dict(href=url))) self.assertThat(page, anchor_matcher) def getViewContentXmlHttpRequest(self, context, view_name, person): # Helper method to request a view via an XMLHttpRequest. with person_logged_in(person): request = LaunchpadTestRequest( HTTP_X_REQUESTED_WITH='XMLHttpRequest') view = create_initialized_view(context, view_name, request=request) return view() def test_blacklist_options(self): # Blacklist options are presented to the users who are archive # admins. ds_diff = self.factory.makeDistroSeriesDifference() archive_admin = self.factory.makeArchiveAdmin( archive=ds_diff.derived_series.main_archive) view_content = self.getViewContentXmlHttpRequest( ds_diff, '+listing-distroseries-extra', archive_admin) soup = BeautifulSoup(view_content) self.assertEqual( 1, len(soup.findAll('div', {'class': 'blacklist-options'}))) def test_blacklist_options_disabled(self): # Blacklist options are disabled to the users who are *not* archive # admins. ds_diff = self.factory.makeDistroSeriesDifference() person = self.factory.makePerson() view_content = self.getViewContentXmlHttpRequest( ds_diff, '+listing-distroseries-extra', person) soup = BeautifulSoup(view_content) self.assertEqual( 1, len(soup.findAll('div', {'class': 'blacklist-options-disabled'}))) def test_blacklist_options_initial_values_none(self): ds_diff = self.factory.makeDistroSeriesDifference() view = create_initialized_view(ds_diff, '+listing-distroseries-extra') # If the difference is not currently blacklisted, 'NONE' is set # as the default value for the field. self.assertEqual('NONE', view.initial_values.get('blacklist_options')) def test_blacklist_options_initial_values_current(self): ds_diff = self.factory.makeDistroSeriesDifference( status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') self.assertEqual(DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT, view.initial_values.get('blacklist_options')) def test_blacklist_options_initial_values_always(self): ds_diff = self.factory.makeDistroSeriesDifference( status=DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') self.assertEqual(DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS, view.initial_values.get('blacklist_options')) def test_package_diff_request_link(self): # The link to compute package diffs is only shown to # a user with lp.Edit permission (i.e. lp.View on the # distribution). ds_diff = self.factory.makeDistroSeriesDifference( set_base_version=True) package_diff_request_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Request link', 'a', text=re.compile( '\s*Compute differences from last common version\s*'))) with person_logged_in(self.factory.makePerson()): self.assertTrue(check_permission('launchpad.Edit', ds_diff)) self.assertTrue( check_permission('launchpad.View', ds_diff.derived_series.parent)) view = create_initialized_view(ds_diff, '+listing-distroseries-extra') self.assertThat(view(), package_diff_request_matcher) self.assertTrue(view.show_package_diffs_request_link) package_diff_header_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Package diffs header', 'dt', text=re.compile('\s*Differences from last common version:'))) package_diff_info_matcher = soupmatchers.HTMLContains( soupmatchers.Within( soupmatchers.Tag('Package diff container', 'dd'), soupmatchers.Tag('Package diffs info', 'ul', attrs={'class': 'package-diff-status'}))) def test_package_diff_label(self): # If base_version is not None the label for the section is # there. changelog_lfa = self.factory.makeChangelog('foo', ['0.30-1']) parent_changelog_lfa = self.factory.makeChangelog( 'foo', ['0.32-1', '0.30-1']) transaction.commit() # Yay, librarian. ds_diff = self.factory.makeDistroSeriesDifference( versions={ 'derived': '0.30-1', 'parent': '0.32-1', }, changelogs={ 'derived': changelog_lfa, 'parent': parent_changelog_lfa }) with celebrity_logged_in('admin'): ds_diff.parent_package_diff = self.factory.makePackageDiff() ds_diff.package_diff = self.factory.makePackageDiff() view = create_initialized_view(ds_diff, '+listing-distroseries-extra') html = view() self.assertThat(html, self.package_diff_header_matcher) def test_package_diff_no_base_version(self): # If diff's base_version is None packages diffs are not displayed # and neither is the link to compute them. versions = { 'base': None, # No base version. 'derived': '0.1-1derived1', 'parent': '0.1-2' } ds_diff = self.factory.makeDistroSeriesDifference(versions=versions) package_diff_request_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Request link', 'a', text=re.compile( '\s*Compute differences from last common version\s*'))) pending_package_diff_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Pending package diff', 'span', attrs={'class': 'PENDING'})) unknown_base_version = soupmatchers.HTMLContains( soupmatchers.Tag( 'Unknown base version', 'dd', text=re.compile('\s*Unknown, so no diffs are available'))) with celebrity_logged_in('admin'): view = create_initialized_view(ds_diff, '+listing-distroseries-extra') html = view() self.assertFalse(view.show_package_diffs_request_link) self.assertThat(html, unknown_base_version) self.assertThat( html, Not( MatchesAny(package_diff_request_matcher, pending_package_diff_matcher, self.package_diff_header_matcher))) def test_package_diffs_hidden_to_unprivileged_user_if_not_available(self): # No diff information is shown if pacakge diffs are not available and # the user does not have permission to request their creation. ds_diff = self.factory.makeDistroSeriesDifference( versions={ 'base': '0.1', 'derived': '0.1-1derived1', 'parent': '0.1-2' }, set_base_version=True) with anonymous_logged_in(): view = create_initialized_view(ds_diff, '+listing-distroseries-extra') html = view() self.assertThat(html, Not(self.package_diff_header_matcher)) self.assertThat(html, Not(self.package_diff_info_matcher))
attrs={'class': 'breadcrumbs'}) webhooks_page_crumb_tag = soupmatchers.Tag('webhooks page breadcrumb', 'li', text=re.compile('Webhooks')) webhooks_collection_crumb_tag = soupmatchers.Tag( 'webhooks page breadcrumb', 'a', text=re.compile('Webhooks'), attrs={'href': re.compile(r'/\+webhooks$')}) add_webhook_tag = soupmatchers.Tag( 'add webhook', 'a', text='Add webhook', attrs={'href': re.compile(r'/\+new-webhook$')}) webhook_listing_constants = soupmatchers.HTMLContains( soupmatchers.Within(breadcrumbs_tag, webhooks_page_crumb_tag), add_webhook_tag) webhook_listing_tag = soupmatchers.Tag('webhook listing', 'table', attrs={'class': 'listing'}) batch_nav_tag = soupmatchers.Tag('batch nav links', 'td', attrs={'class': 'batch-navigation-links'}) class GitRepositoryTestHelpers: event_type = "git:push:0.1" expected_event_types = [ ("git:push:0.1", "Git push"),