def test_blog_posts(self): """Posts from the launchpad blog are shown when feature is enabled""" self.useFixture(FeatureFixture({'app.root_blog.enabled': True})) posts = [ self._make_blog_post(1, "A post", "Post contents.", "2002"), self._make_blog_post(2, "Another post", "More contents.", "2003"), ] calls = [] def _get_blog_posts(): calls.append('called') return posts root = getUtility(ILaunchpadRoot) with anonymous_logged_in(): view = create_initialized_view(root, 'index.html') view.getRecentBlogPosts = _get_blog_posts result = view() markup = BeautifulSoup( result, parse_only=SoupStrainer(id='homepage-blogposts')) self.assertEqual(['called'], calls) items = markup.findAll('li', 'news') # Notice about launchpad being opened is always added at the end self.assertEqual(3, len(items)) a = items[-1].find("a") self.assertEqual("Launchpad now open source", a.string.strip()) for post, item in zip(posts, items): a = item.find("a") self.assertEqual(post['link'], a["href"]) self.assertEqual(post['title'], a.string)
def getRemoteBug(self, bug_id): """See `ExternalBugTracker`.""" # Only parse tables to save time and memory. If we didn't have # to check for application errors in the page (using # _checkForApplicationError) then we could be much more # specific than this. bug_page = BeautifulSoup(self._getPage('view.php?id=%s' % bug_id).content, convertEntities=BeautifulSoup.HTML_ENTITIES, parseOnlyThese=SoupStrainer('table')) app_error = self._checkForApplicationError(bug_page) if app_error: app_error_code, app_error_message = app_error # 1100 is ERROR_BUG_NOT_FOUND in Mantis (see # mantisbt/core/constant_inc.php). if app_error_code == '1100': return None, None else: raise BugWatchUpdateError("Mantis APPLICATION ERROR #%s: %s" % (app_error_code, app_error_message)) bug = { 'id': bug_id, 'status': self._findValueRightOfKey(bug_page, 'Status'), 'resolution': self._findValueRightOfKey(bug_page, 'Resolution') } return int(bug_id), bug
def get_feedback_messages(content): """Find and return the feedback messages of the page.""" message_classes = [ 'message', 'informational message', 'error message', 'warning message' ] soup = BeautifulSoup(content, parseOnlyThese=SoupStrainer( ['div', 'p'], {'class': message_classes})) return [extract_text(tag) for tag in soup]
def test_featured_projects_manage_link_requires_edit(self): self.setUpRegistryExpert() view = create_initialized_view(self.root, 'index.html', principal=self.expert) # Stub out the getRecentBlogPosts which fetches a blog feed using # urlfetch. view.getRecentBlogPosts = lambda: [] content = BeautifulSoup(view(), parse_only=SoupStrainer('a')) self.assertTrue( content.find('a', href='+featuredprojects'), "Cannot find the +featuredprojects link on the first page")
def test_has_logo_without_watermark(self): root = getUtility(ILaunchpadRoot) user = self.factory.makePerson() login_person(user) view = create_initialized_view(root, 'index.html', principal=user) # Replace the blog posts so the view does not make a network request. view.getRecentBlogPosts = lambda: [] markup = BeautifulSoup(view(), parse_only=SoupStrainer(id='document')) self.assertIs(False, view.has_watermark) self.assertIs(None, markup.find(True, id='watermark')) logo = markup.find(True, id='launchpad-logo-and-name') self.assertIsNot(None, logo) self.assertEqual('/@@/launchpad-logo-and-name.png', logo['src'])
def test_blog_posts_with_memcache(self): self.useFixture(FeatureFixture({'app.root_blog.enabled': True})) posts = [ self._make_blog_post(1, "A post", "Post contents.", "2002"), self._make_blog_post(2, "Another post", "More contents.", "2003"), ] key = '%s:homepage-blog-posts' % config.instance_name getUtility(IMemcacheClient).set(key, posts) root = getUtility(ILaunchpadRoot) with anonymous_logged_in(): view = create_initialized_view(root, 'index.html') result = view() markup = BeautifulSoup( result, parse_only=SoupStrainer(id='homepage-blogposts')) items = markup.findAll('li', 'news') self.assertEqual(3, len(items))
def find_tags_by_class(content, class_, only_first=False): """Find and return one or more tags matching the given class(es)""" match_classes = set(class_.split()) def class_matcher(value): if value is None: return False classes = set(value.split()) return match_classes.issubset(classes) soup = BeautifulSoup( content, parseOnlyThese=SoupStrainer(attrs={'class': class_matcher})) if only_first: find = BeautifulSoup.find else: find = BeautifulSoup.findAll return find(soup, attrs={'class': class_matcher})
def find_tag_by_id(content, id): """Find and return the tag with the given ID""" if isinstance(content, PageElement): elements_with_id = content.findAll(True, {'id': id}) elif isinstance(content, PageElement4): elements_with_id = content.find_all(True, {'id': id}) else: elements_with_id = [ tag for tag in BeautifulSoup(content, parseOnlyThese=SoupStrainer(id=id)) ] if len(elements_with_id) == 0: return None elif len(elements_with_id) == 1: return elements_with_id[0] else: raise DuplicateIdError('Found %d elements with id %r' % (len(elements_with_id), id))
def test_blog_disabled(self): """Launchpad blog not queried for display without feature""" calls = [] def _get_blog_posts(): calls.append('called') return [] root = getUtility(ILaunchpadRoot) user = self.factory.makePerson() login_person(user) view = create_initialized_view(root, 'index.html', principal=user) view.getRecentBlogPosts = _get_blog_posts markup = BeautifulSoup(view(), parse_only=SoupStrainer(id='homepage')) self.assertEqual([], calls) self.assertIs(None, markup.find(True, id='homepage-blogposts')) # Even logged in users should get the launchpad intro text in the left # column rather than blank space when the blog is not being displayed. self.assertTrue(view.show_whatslaunchpad) self.assertTrue(markup.find(True, 'homepage-whatslaunchpad'))