def test_distributionpage_series_list_noadmin(self): """Verify that a non-admin does see the series list when there is a series. """ self.factory.makeDistroSeries(distribution=self.distro, status=SeriesStatus.CURRENT) login_person(self.simple_user) view = create_initialized_view(self.distro, '+index', principal=self.simple_user) add_series_match = soupmatchers.HTMLContains( soupmatchers.Tag('link to add a series', 'a', attrs={ 'href': canonical_url(self.distro, view_name='+addseries') }, text='Add series')) series_header_match = soupmatchers.HTMLContains( soupmatchers.Tag('Active series and milestones widget', 'h2', text='Active series and milestones')) self.assertThat(view.render(), series_header_match) self.assertThat(view.render(), Not(add_series_match))
def test_view_banner(self): """The privacy banner should contain a noscript message""" owner = self.factory.makePerson() policy = SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY product = self.factory.makeProduct(owner=owner, specification_sharing_policy=policy) spec = self.factory.makeSpecification( information_type=InformationType.PROPRIETARY, owner=owner, product=product) privacy_banner = soupmatchers.Tag( 'privacy-banner', True, text=re.compile('The information on this page is private')) getUtility(IService, 'sharing').ensureAccessGrants([owner], owner, specifications=[spec], ignore_permissions=True) browser = self.getViewBrowser(spec, '+index', user=owner) self.assertThat(browser.contents, soupmatchers.HTMLContains(privacy_banner)) browser = self.getViewBrowser(spec, '+subscribe', user=owner) self.assertThat(browser.contents, soupmatchers.HTMLContains(privacy_banner))
def test_job_notifications_display_in_progress(self): job = self.makeJob('package_1', failed=False) with person_logged_in(self.archive.owner): view = create_initialized_view(self.archive, "+packages", principal=self.archive.owner) html = view.render() packages_matches = soupmatchers.HTMLContains( soupmatchers.Tag('job summary', 'a', text=re.compile('Copying.*'), attrs={'class': re.compile('job-summary')}), soupmatchers.Tag('copied from', 'a', text=job.source_archive.displayname, attrs={'class': re.compile('copied-from')}), ) packages_not_matches = soupmatchers.HTMLContains( # Check the absence of the link remove the notification. soupmatchers.Tag( 'remove notification link', 'a', text=re.compile('\s*Remove notification\s*'), attrs={'class': re.compile('remove-notification')}), ) self.assertThat(html, packages_matches) self.assertThat(html, Not(packages_not_matches))
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_few_hooks(self): # The table is just a simple table if there is only one batch. link_matchers = self.makeHooksAndMatchers(3) self.assertThat( self.makeView("+webhooks")(), MatchesAll( webhook_listing_constants, soupmatchers.HTMLContains(webhook_listing_tag, *link_matchers), Not(soupmatchers.HTMLContains(batch_nav_tag))))
def test_many_hooks(self): # Batch navigation controls are shown once there are enough. link_matchers = self.makeHooksAndMatchers(10) self.assertThat( self.makeView("+webhooks")(), MatchesAll( webhook_listing_constants, soupmatchers.HTMLContains(webhook_listing_tag, batch_nav_tag, *link_matchers[:5]), Not(soupmatchers.HTMLContains(*link_matchers[5:]))))
def test_mirrors_links(self): view = create_initialized_view(self.distro, "+index") cd_mirrors_link = soupmatchers.HTMLContains( soupmatchers.Tag("CD mirrors link", "a", text="CD mirrors")) archive_mirrors_link = soupmatchers.HTMLContains( soupmatchers.Tag("Archive mirrors link", "a", text="Archive mirrors")) self.assertThat(view(), Not(MatchesAny(cd_mirrors_link, archive_mirrors_link))) with admin_logged_in(): self.distro.supports_mirrors = True self.assertThat(view(), MatchesAll(cd_mirrors_link, archive_mirrors_link))
def test_build_history_private_build_display(self): self.createRecipeBuildWithBuilder(builder=self.builder) self.createRecipeBuildWithBuilder(private_branch=True, builder=self.builder) view = create_initialized_view(self.builder, '+history') private_build_icon_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Private build icon', 'img', attrs={'src': '/@@/private'})) private_build_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Private build', 'td', text='Private job')) self.assertThat( view.render(), MatchesAll(private_build_matcher, private_build_icon_matcher))
def test_golang_meta_renders(self): # ensure golang meta import path is rendered if project has # bzr default vcs and default branch set. # See: https://golang.org/cmd/go/#hdr-Remote_import_paths owner = self.factory.makePerson(name='zardoz') product = self.factory.makeProduct(name='wapcaplet') series = self.factory.makeProductSeries(owner=owner, product=product, name='a-series') branch = self.factory.makeBranch(product=product, name='a-branch', owner=owner) view = create_initialized_view(series, '+index') with person_logged_in(series.product.owner): series.branch = branch series.product.vcs = VCSType.BZR golang_import = ("{hostname}/wapcaplet/a-series bzr " "{root_url}~zardoz/wapcaplet/a-branch").format( hostname=config.vhost.mainsite.hostname, root_url=allvhosts.configs['mainsite'].rooturl, ) self.assertEqual(golang_import, view.golang_import_spec) meta_tag = soupmatchers.Tag('go-import-meta', 'meta', attrs={ 'name': 'go-import', 'content': golang_import }) browser = self.getViewBrowser(series, '+index', user=series.branch.owner) self.assertThat(browser.contents, soupmatchers.HTMLContains(meta_tag))
def test_job_notifications_display_multiple(self): job1 = self.makeJob('package_1') job2 = self.makeJob('package_2', failed=True) job3 = self.makeJob('package_3') with person_logged_in(self.archive.owner): view = create_initialized_view(self.archive, "+packages", principal=self.archive.owner) html = view.render() packages_matches = soupmatchers.HTMLContains( soupmatchers.Tag('job1', 'div', attrs={ 'class': 'pending-job', 'job_id': job1.id }), soupmatchers.Tag('job2', 'div', attrs={ 'class': 'pending-job', 'job_id': job2.id }), soupmatchers.Tag('job3', 'div', attrs={ 'class': 'pending-job', 'job_id': job3.id }), ) self.assertThat(html, packages_matches) self.assertEqual([], BeautifulSoup(html).findAll( 'span', text=re.compile('Showing 5 of .')))
def test_index(self): """Test the index page of a branch merge queue.""" with person_logged_in(ANONYMOUS): queue = self.factory.makeBranchMergeQueue() queue_owner = queue.owner.displayname queue_registrant = queue.registrant.displayname queue_description = queue.description queue_url = canonical_url(queue) branch = self.factory.makeBranch() branch_name = branch.bzr_identity with person_logged_in(branch.owner): branch.addToQueue(queue) # XXX: rockstar - bug #666979 - The text argument should really ignore # whitespace, but it currently doesn't. Now I have two problems. queue_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Page title', 'h1', text=re.compile('\w*%s queue owned by %s\w*' % ( queue.name, queue.owner.displayname))), soupmatchers.Tag( 'Description Label', 'dt', text=re.compile('\w*Description\w*')), soupmatchers.Tag( 'Description Value', 'dd', text=re.compile('\w*%s\w*' % queue.description)), soupmatchers.Tag( 'Branch link', 'a', text=re.compile('\w*%s\w*' % branch.bzr_identity))) browser = self.getUserBrowser(canonical_url(queue), user=queue.owner) self.assertThat(browser.contents, queue_matcher)
def test_show_sponsor(self): archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) ppa = self.factory.makeArchive(distribution=archive.distribution) spph = self.factory.makeSourcePackagePublishingHistory(archive=ppa) creator = self.factory.makePerson() sponsor = self.factory.makePerson() copied_spph = spph.copyTo(spph.distroseries, spph.pocket, archive, creator=creator, sponsor=sponsor) html = create_initialized_view(copied_spph, "+record-details").render() record_matches = soupmatchers.HTMLContains( soupmatchers.Tag("copy summary", "li", text=re.compile("sponsored by")), soupmatchers.Tag("copy creator", "a", text=creator.displayname, attrs={ "href": "/~%s" % creator.name, "class": "sprite person", }), soupmatchers.Tag("copy sponsor", "a", text=sponsor.displayname, attrs={ "href": "/~%s" % sponsor.name, "class": "sprite person", }), ) self.assertThat(html, record_matches)
def test_create(self): """Test that branch merge queues can be created from a branch.""" self.enable_queue_flag() with person_logged_in(ANONYMOUS): rockstar = self.factory.makePerson(name='rockstar') branch = self.factory.makeBranch(owner=rockstar) self.factory.makeBranch(product=branch.product) owner_name = branch.owner.name browser = self.getUserBrowser(canonical_url(branch), user=rockstar) # There shouldn't be a merge queue linked here. noqueue_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Not managed', 'div', text=re.compile( '\w*This branch is not managed by a queue.\w*'))) self.assertThat(browser.contents, noqueue_matcher) browser.getLink('Create a new queue').click() browser.getControl('Name').value = 'libbob-queue' browser.getControl('Description').value = ( 'This is a queue for the libbob projects.') browser.getControl('Create Queue').click() self.assertEqual( 'http://code.launchpad.dev/~rockstar/+merge-queues/libbob-queue', browser.url)
def test_job_notifications_display_failed(self): job = self.makeJob('package_1', failed=True) # Manually poke an error message. removeSecurityProxy(job).extendMetadata( {'error_message': 'Job failed!'}) with person_logged_in(self.archive.owner): view = create_initialized_view(self.archive, "+packages", principal=self.archive.owner) html = view.render() packages_matches = soupmatchers.HTMLContains( # Check the main title. soupmatchers.Tag('job summary', 'a', text=re.compile('Copying.*'), attrs={'class': re.compile('job-summary')}), # Check the link to the source archive. soupmatchers.Tag('copied from', 'a', text=job.source_archive.displayname, attrs={'class': re.compile('copied-from')}), # Check the presence of the link to remove the notification. soupmatchers.Tag( 'no remove notification link', 'a', text=re.compile('\s*Remove notification\s*'), attrs={'class': re.compile('remove-notification')}), # Check the presence of the error message. soupmatchers.Tag( 'job error msg', 'div', text='Job failed!', attrs={'class': re.compile('job-failed-error-msg')}), ) self.assertThat(html, packages_matches)
def test_view_with_source_package_recipe(self): # When a SourcePackageRelease is linked to a # SourcePackageRecipeBuild, the view shows which recipe was # responsible for creating the SPR. sprb = self.factory.makeSourcePackageRecipeBuild(archive=self.archive) recipe = sprb.recipe requester = sprb.requester spph = self.publisher.getPubSource( archive=self.archive, status=PackagePublishingStatus.PUBLISHED) spph.sourcepackagerelease.source_package_recipe_build = sprb recipe_link_matches = soupmatchers.HTMLContains( soupmatchers.Tag('link to build', 'a', attrs={'href': canonical_url(sprb)}, text='Built'), soupmatchers.Tag('recipe name', 'a', attrs={'href': canonical_url(recipe)}, text=recipe.name), soupmatchers.Tag('requester', 'a', attrs={'href': canonical_url(requester)}, text=requester.displayname)) browser = self.getViewBrowser(spph, '+listing-archive-extra') self.assertThat(browser.contents, recipe_link_matches)
def test_branch_list_h1(self): self.makeABranch() page = self.get_branch_list_page() h1_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Title', 'h1', text='Bazaar Branches of Bambam owned by Barney')) self.assertThat(page, h1_matcher)
def test_branch_list_empty(self): page = self.get_branch_list_page() empty_message_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Empty message', 'p', text='There are no branches related to Barney ' 'in Launchpad today.')) self.assertThat(page, empty_message_matcher)
def test_builds_link(self): view = create_initialized_view(self.distro, "+index") builds_link = soupmatchers.HTMLContains( soupmatchers.Tag("Builds link", "a", text="Builds")) self.assertThat(view(), Not(builds_link)) with admin_logged_in(): self.distro.official_packages = True self.assertThat(view(), builds_link)
def test_revision_id(self): build = self.factory.makeSnapBuild() build.updateStatus( BuildStatus.FULLYBUILT, slave_status={"revision_id": "dummy"}) build_view = create_initialized_view(build, "+index") self.assertThat(build_view(), soupmatchers.HTMLContains( soupmatchers.Tag( "revision ID", "li", attrs={"id": "revision-id"}, text=re.compile(r"^\s*Revision: dummy\s*$"))))
def test_branch_list_owned_link(self): # The link to the owned branches is always displayed. owned_branches_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Owned link', 'a', text='Owned branches', attrs={'href': self.code_base_url})) page = self.get_branch_list_page(page_name='+subscribedbranches') self.assertThat(page, owned_branches_matcher)
def test_store_upload_status_in_progress(self): build = self.factory.makeSnapBuild(status=BuildStatus.FULLYBUILT) getUtility(ISnapStoreUploadJobSource).create(build) build_view = create_initialized_view(build, "+index") self.assertThat(build_view(), soupmatchers.HTMLContains( soupmatchers.Tag( "store upload status", "li", attrs={"id": "store-upload-status"}, text=re.compile(r"^\s*Store upload in progress\s*$"))))
def test_ppas_link(self): view = create_initialized_view(self.distro, "+index") ppas_link = soupmatchers.HTMLContains( soupmatchers.Tag("PPAs link", "a", text="Personal Package Archives")) self.assertThat(view(), Not(ppas_link)) with admin_logged_in(): self.distro.supports_ppas = True self.assertThat(view(), ppas_link)
def test_branch_list_activereviews_link(self): # The link to the active reviews is always displayed. active_review_matcher = soupmatchers.HTMLContains( soupmatchers.Tag('Active reviews link', 'a', text='Active reviews', attrs={'href': self.base_url + '/+activereviews'})) page = self.get_branch_list_page() self.assertThat(page, active_review_matcher)
def test_meaningful_branch_name(self): """The displayed branch name should include the unique name.""" branch = self.factory.makeProductBranch() series = self.factory.makeProductSeries(branch=branch) tag = soupmatchers.Tag('series-branch', 'a', attrs={'id': 'series-branch'}, text='lp://dev/' + branch.unique_name) browser = self.getViewBrowser(series) self.assertThat(browser.contents, soupmatchers.HTMLContains(tag))
def test_proprietary_hides_packaging(self): """Proprietary, Embargoed lack "Distribution packaging" sections.""" product = self.factory.makeProduct( information_type=InformationType.PROPRIETARY) series = self.factory.makeProductSeries(product=product) browser = self.getBrowser(series) tag = soupmatchers.Tag('portlet-packages', True, attrs={'id': 'portlet-packages'}) self.assertThat(browser.contents, Not(soupmatchers.HTMLContains(tag)))
def assertBatches(self, context, link_matchers, batched, start, size): view = create_initialized_view(context, "+snaps") listing_tag = soupmatchers.Tag("snap listing", "table", attrs={"class": "listing sortable"}) batch_nav_tag = soupmatchers.Tag( "batch nav links", "td", attrs={"class": "batch-navigation-links"}) present_links = ([batch_nav_tag] if batched else []) + [ matcher for i, matcher in enumerate(link_matchers) if i in range(start, start + size) ] absent_links = ([] if batched else [batch_nav_tag]) + [ matcher for i, matcher in enumerate(link_matchers) if i not in range(start, start + size) ] self.assertThat( view.render(), MatchesAll(soupmatchers.HTMLContains(listing_tag, *present_links), Not(soupmatchers.HTMLContains(*absent_links))))
def test_branch_list_subscribed_link(self): # The link to the subscribed branches is always displayed. subscribed_branches_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Subscribed link', 'a', text='Subscribed branches', attrs={'href': self.base_url + '/+subscribedbranches'})) page = self.get_branch_list_page() self.assertThat(page, subscribed_branches_matcher)
def test_distributionpage_addseries_link_noadmin(self): """Verify that a non-admin does not see the +addseries link nor the series header (since there is no series yet). """ login_person(self.simple_user) view = create_initialized_view( self.distro, '+index', principal=self.simple_user) add_series_match = soupmatchers.HTMLContains( soupmatchers.Tag( 'link to add a series', 'a', attrs={'href': canonical_url(self.distro, view_name='+addseries')}, text='Add series')) series_header_match = soupmatchers.HTMLContains( soupmatchers.Tag( 'Active series and milestones widget', 'h2', text='Active series and milestones')) self.assertThat( view.render(), Not(MatchesAny(add_series_match, series_header_match)))
def _assertNoRequestLink(self, ds_diff): view = create_initialized_view(ds_diff, '+listing-distroseries-extra') package_diff_request_matcher = soupmatchers.HTMLContains( soupmatchers.Tag( 'Request link', 'a', text=re.compile( '\s*Compute differences from last common version\s*'))) self.assertFalse(view.show_package_diffs_request_link) self.assertThat(view(), Not(package_diff_request_matcher))
def test_reassign_distro_page_title(self): # Reassign should say maintainer instead of owner. admin = login_celebrity('admin') distribution = self.factory.makeDistribution( name="boobuntu", owner=self.owner, registrant=self.registrant) view = create_initialized_view( distribution, '+reassign', principal=admin, method='GET') header_match = soupmatchers.HTMLContains( soupmatchers.Tag( 'Header should say maintainer (not owner)', 'h1', text='Change the maintainer of Boobuntu')) self.assertThat(view.render(), header_match)