Merge pull request #2900 from mozilla/feature/upgrade-lxml

Upgrade pyquery, lxml. Add cssselect dependency for lxml.
This commit is contained in:
Christopher Grebs 2016-06-22 11:10:29 +02:00 коммит произвёл GitHub
Родитель fdf9403675 6f656b5db0
Коммит 3998d4349a
19 изменённых файлов: 74 добавлений и 93 удалений

Просмотреть файл

@ -17,10 +17,15 @@ Pillow==3.2.0 \
--hash=sha256:a1be6f04dd631c511fd8cef83d610dafb755e97e25fbf5a03fb1fd5ee1aa4745 \
--hash=sha256:cc44c54b432b27c59e3d0bc048a5a7581b13944c14c56c94634cfe8d5eabade5
# lxml is required by pyquery
lxml==2.2.6 \
--hash=sha256:7fd36e4a56360cd5d7319e357b04a90e2c6b836ea220c88f9451c300ae33cc5e
m2crypto==0.22.3 \
--hash=sha256:6071bfc817d94723e9b458a010d565365104f84aa73f7fe11919871f7562ff72
lxml==3.6.0 \
--hash=sha256:8fdfeea3f1854bfd117bf0a1f13e14c3377bf14c1b7191f497f368df08dcbb55 \
--hash=sha256:4566e8d80f13ade36e55ac60c9bb9a3540115a6314af5692da09d01a15795728 \
--hash=sha256:9c74ca28a7f0c30dca8872281b3c47705e21217c8bc63912d95c9e2a7cac6bdf \
--hash=sha256:bea9bcf0467ab8741bfaf31dab1fd114984312f2b11daaf2ff8610fad8869e7b \
--hash=sha256:3879d0de2d616041a25a76b0d39898fabbb2ce95d30d3babb8920be378754fea
m2crypto==0.22.3 --hash=sha256:a56589986acb39c785a497766b1512ddbfa118bd5ae6e722ef0d96381a87fcf7 \
--hash=sha256:7929e35163f316751fe2d33dd1405729e3fa709f94fc1a29989efe8271705f39 \
--hash=sha256:6071bfc817d94723e9b458a010d565365104f84aa73f7fe11919871f7562ff72
# simplejson is required by amo-validator
simplejson==3.8.2 \
--hash=sha256:d34db551b129650c5a048170a57b34e6dd8b53439acee5287e2efa2e53c80d33 \

Просмотреть файл

@ -214,8 +214,9 @@ pycparser==2.14 \
--hash=sha256:7959b4a74abdc27b312fed1c21e6caf9309ce0b29ea86b591fd2e99ecdf27f73
pyhs2==0.6.0 \
--hash=sha256:16aef41cc4e3d9a4a3e3a597481451aa8b26ba3e68bda2c820b1fef708206e21
pyquery==0.4 \
--hash=sha256:88652c572da27dc53f9f3023bf924eec563293691e0851b63f7b858110a27b1d
pyquery==1.2.13 \
--hash=sha256:2c89331bd9524fefba9e771a94de8cd4668fb7349f27bfa1bbe57323dd06bc14 \
--hash=sha256:fbc95cf422ac79fa00c5107a2f33dff7dd106d6de569493bd938881b75d42e49
# python-dateutil is required by elasticsearch-dsl
python-dateutil==1.5 \
--hash=sha256:6f197348b46fb8cdf9f3fcfc2a7d5a97da95db3e2e8667cf657216274fe1b009
@ -276,3 +277,5 @@ setuptools==23.0.0 \
--hash=sha256:034d5fdd7d2bf32e88218ffe86cde8af5ecbaa3361d334b2bd48a4060eac3816 \
--hash=sha256:699b03e5faed482d642fc706e568749537eb72be7e6ede8dbee5135593d2724e \
--hash=sha256:8f8a04dc507975cb4e43e7663823643a41f5035b9bf104d7a20222a35f976724
cssselect==0.9.1 \
--hash=sha256:0535a7e27014874b27ae3a4d33e8749e345bdfa62766195208b7996bf1100682

Просмотреть файл

@ -58,7 +58,7 @@ def _test_hovercards(self, doc, addons, src=''):
addons = list(addons)
assert doc.find('.addon.hovercard').length == len(addons)
for addon in addons:
btn = doc.find('.install[data-addon=%s]' % addon.id)
btn = doc.find('.install[data-addon="%s"]' % addon.id)
assert btn.length == 1
hc = btn.parents('.addon.hovercard')
assert hc.find('a').attr('href') == (

Просмотреть файл

@ -1,29 +0,0 @@
"""
If libxml2 finds a <noscript> element in <head>, it appears to create a <body>
block right there and drop our real <body>, which has nice attributes we'd like
to see. So we'll regex out all those nasty <noscript>s and pretend everything
is just right.
The libxml2 bug is https://bugzilla.gnome.org/show_bug.cgi?id=615785.
"""
import re
import pyquery
# Yes, we're munging HTML with a regex. Deal with it.
noscript_re = re.compile('<noscript>.*?</noscript>')
def remove_noscript_from_head(html):
head_end = html.find('</head>')
new_head = noscript_re.sub('', html[:head_end])
return new_head + html[head_end:]
class PyQuery(pyquery.PyQuery):
def __init__(self, *args, **kwargs):
if (args and isinstance(args[0], basestring) and
not args[0].startswith('http')):
args = (remove_noscript_from_head(args[0]),) + args[1:]
super(PyQuery, self).__init__(*args, **kwargs)

Просмотреть файл

@ -18,7 +18,6 @@ from olympia.amo.tests import TestCase
from olympia.access import acl
from olympia.access.models import Group, GroupUser
from olympia.addons.models import Addon, AddonUser
from olympia.amo.pyquery_wrapper import PyQuery
from olympia.amo.tests import check_links, WithDynamicEndpoints
from olympia.amo.urlresolvers import reverse
from olympia.users.models import UserProfile
@ -230,7 +229,7 @@ class TestOtherStuff(TestCase):
def test_heading(self):
def title_eq(url, alt, text):
response = self.client.get(url, follow=True)
doc = PyQuery(response.content)
doc = pq(response.content)
assert alt == doc('.site-title img').attr('alt')
assert text == doc('.site-title').text()
@ -241,7 +240,7 @@ class TestOtherStuff(TestCase):
def test_login_link(self):
r = self.client.get(reverse('home'), follow=True)
doc = PyQuery(r.content)
doc = pq(r.content)
next = urllib.urlencode({'to': '/en-US/firefox/'})
assert '/en-US/firefox/users/login?%s' % next == (
doc('.account.anonymous a')[1].attrib['href'])

Просмотреть файл

@ -339,10 +339,14 @@ class TestFeeds(TestCase):
rss_doc = pq(r.content)
pg_items = doc('.items .item')
rss_items = rss_doc('item')
# We have to set `parser=xml` because of
# https://github.com/gawel/pyquery/issues/93
items_urls = zip(
sorted((absolutify(pq(x).find('h3 a').attr('href')), pq(x))
for x in pg_items),
sorted((pq(x).find('link').text(), pq(x)) for x in rss_items))
sorted((pq(x).find('link').text(), pq(x, parser='xml'))
for x in rss_items))
for (pg_url, pg_item), (rss_url, rss_item) in items_urls:
abs_url = pg_url.split('?')[0]
assert rss_url.endswith(abs_url), 'Unexpected URL: %s' % abs_url

Просмотреть файл

@ -189,14 +189,14 @@ class TestReporterDetail(TestCase):
assert reports.find('.bad').text().split()[0] == str(bad)
# Check "Filter by Application" field.
option = doc('#compat-form select[name=appver] option[selected]')
option = doc('#compat-form select[name="appver"] option[selected]')
assert option.val() == appver
return r
def test_appver_all(self):
self._generate()
self.check_table(
good=3, bad=7, appver='',
good=3, bad=7, appver=None,
report_pks=[idx for idx, val in enumerate(self.reports)])
def test_firefox_single(self):
@ -259,7 +259,7 @@ class TestReporterDetail(TestCase):
guid=self.addon.guid, app_guid=app_guid, app_version='0.9.3',
works_properly=True)
self.reports.append(report.pk)
r = self.check_table(good=1, bad=0, appver='', report_pks=[0])
r = self.check_table(good=1, bad=0, appver=None, report_pks=[0])
msg = 'Unknown (%s)' % app_guid
assert msg in r.content, 'Expected %s in body' % msg
@ -270,7 +270,7 @@ class TestReporterDetail(TestCase):
self.addon.update(is_listed=False)
self._generate()
self.check_table(
good=3, bad=7, appver='',
good=3, bad=7, appver=None,
report_pks=[idx for idx, val in enumerate(self.reports)])
@mock.patch('olympia.compat.views.owner_or_unlisted_reviewer',
@ -280,7 +280,7 @@ class TestReporterDetail(TestCase):
self.addon.update(is_listed=False)
self._generate()
self.check_table(
good=0, bad=0, appver='',
good=0, bad=0, appver=None,
report_pks=[])

Просмотреть файл

@ -161,8 +161,8 @@ class TestDashboard(HubTest):
def get_action_links(self, addon_id):
r = self.client.get(self.url)
doc = pq(r.content)
links = [a.text.strip() for a in
doc('.item[data-addonid=%s] .item-actions li > a' % addon_id)]
selector = '.item[data-addonid="%s"] .item-actions li > a' % addon_id
links = [a.text.strip() for a in doc(selector)]
return links
def test_no_addons(self):
@ -217,7 +217,7 @@ class TestDashboard(HubTest):
def test_public_addon(self):
assert self.addon.status == amo.STATUS_PUBLIC
doc = pq(self.client.get(self.url).content)
item = doc('.item[data-addonid=%s]' % self.addon.id)
item = doc('.item[data-addonid="%s"]' % self.addon.id)
assert item.find('h3 a').attr('href') == self.addon.get_dev_url()
assert item.find('p.downloads'), 'Expected weekly downloads'
assert item.find('p.users'), 'Expected ADU'
@ -233,7 +233,7 @@ class TestDashboard(HubTest):
self.addon.addonuser_set.create(user=self.user_profile)
doc = pq(self.client.get(self.url).content)
item = doc('.item[data-addonid=%s]' % self.addon.id)
item = doc('.item[data-addonid="%s"]' % self.addon.id)
e10s_flag = item.find('.e10s-compatibility.e10s-unknown b')
assert e10s_flag
assert e10s_flag.text() == 'Unknown'
@ -241,7 +241,7 @@ class TestDashboard(HubTest):
AddonFeatureCompatibility.objects.create(
addon=self.addon, e10s=amo.E10S_COMPATIBLE)
doc = pq(self.client.get(self.url).content)
item = doc('.item[data-addonid=%s]' % self.addon.id)
item = doc('.item[data-addonid="%s"]' % self.addon.id)
assert not item.find('.e10s-compatibility.e10s-unknown')
e10s_flag = item.find('.e10s-compatibility.e10s-compatible b')
assert e10s_flag
@ -321,7 +321,7 @@ class TestUpdateCompatibility(TestCase):
password='password')
r = self.client.get(self.url)
doc = pq(r.content)
assert not doc('.item[data-addonid=4594] li.compat')
assert not doc('.item[data-addonid="4594"] li.compat')
a = Addon.objects.get(pk=4594)
r = self.client.get(reverse('devhub.ajax.compat.update',
args=[a.slug, a.current_version.id]))
@ -335,7 +335,7 @@ class TestUpdateCompatibility(TestCase):
r = self.client.get(self.url)
doc = pq(r.content)
cu = doc('.item[data-addonid=3615] .tooltip.compat-update')
cu = doc('.item[data-addonid="3615"] .tooltip.compat-update')
assert cu
update_url = reverse('devhub.ajax.compat.update',
@ -343,17 +343,17 @@ class TestUpdateCompatibility(TestCase):
assert cu.attr('data-updateurl') == update_url
status_url = reverse('devhub.ajax.compat.status', args=[a.slug])
assert doc('.item[data-addonid=3615] li.compat').attr('data-src') == (
status_url)
selector = '.item[data-addonid="3615"] li.compat'
assert doc(selector).attr('data-src') == status_url
assert doc('.item[data-addonid=3615] .compat-update-modal')
assert doc('.item[data-addonid="3615"] .compat-update-modal')
def test_incompat_firefox(self):
versions = ApplicationsVersions.objects.all()[0]
versions.max = AppVersion.objects.get(version='2.0')
versions.save()
doc = pq(self.client.get(self.url).content)
assert doc('.item[data-addonid=3615] .tooltip.compat-error')
assert doc('.item[data-addonid="3615"] .tooltip.compat-error')
def test_incompat_mobile(self):
appver = AppVersion.objects.get(version='2.0')
@ -363,7 +363,7 @@ class TestUpdateCompatibility(TestCase):
av.max = appver
av.save()
doc = pq(self.client.get(self.url).content)
assert doc('.item[data-addonid=3615] .tooltip.compat-error')
assert doc('.item[data-addonid="3615"] .tooltip.compat-error')
class TestDevRequired(TestCase):

Просмотреть файл

@ -1099,7 +1099,7 @@ class TestEditTechnical(TestEdit):
r = self.client.get(self.technical_edit_url)
reqs = pq(r.content)('#required-addons .dependencies')
assert reqs.find('li[data-addonid]').length == 2
req = reqs.find('li[data-addonid=5299]')
req = reqs.find('li[data-addonid="5299"]')
assert req.length == 1
a = req.find('div a')
assert a.attr('href') == addon.get_url_path()

Просмотреть файл

@ -178,7 +178,7 @@ class TestEditLicense(TestOwnership):
# Check that builtin licenses get details links.
doc = pq(unicode(LicenseForm(addon=self.version.addon)))
for license in License.objects.builtins():
radio = 'input.license[value=%s]' % license.builtin
radio = 'input.license[value="%s"]' % license.builtin
assert doc(radio).parent().text() == (
unicode(license.name) + ' Details')
assert doc(radio + '+ a').attr('href') == license.url

Просмотреть файл

@ -725,7 +725,7 @@ class TestCompatibilityResults(TestCase):
assert r.status_code == 200
doc = pq(r.content)
assert doc('time').text()
assert doc('table tr td:eq(1)').text() == 'Firefox 4.0.*'
assert doc('table tr td').eq(1).text() == 'Firefox 4.0.*'
class TestUploadCompatCheck(BaseUploadTest):

Просмотреть файл

@ -428,7 +428,7 @@ class TestVersion(TestCase):
self.addon.latest_version.files.update(status=amo.STATUS_DISABLED)
doc = pq(self.client.get(self.url).content)
buttons = doc('.version-status-actions form button').text()
assert buttons is None
assert not buttons
def test_add_version_modal(self):
r = self.client.get(self.url)

Просмотреть файл

@ -478,7 +478,7 @@ class TestHome(EditorTest):
doc = pq(self.client.get(self.url).content)
cols = doc('#editors-stats .editor-stats-table:eq(1)').find('td')
cols = doc('#editors-stats .editor-stats-table').eq(1).find('td')
assert cols.eq(0).text() == self.user.display_name
assert int(cols.eq(1).text()) == 2 # Approval count should be 2.
@ -503,7 +503,7 @@ class TestHome(EditorTest):
doc = pq(self.client.get(self.url).content)
cols = doc('#editors-stats .editor-stats-table:eq(1)').find('td')
cols = doc('#editors-stats .editor-stats-table').eq(1).find('td')
assert cols.eq(0).text() == self.user.display_name
assert int(cols.eq(1).text()) == 2 # Approval count should be 2.
@ -519,10 +519,11 @@ class TestHome(EditorTest):
def test_stats_user_position_unranked(self):
self.approve_reviews()
doc = pq(self.client.get(self.url).content)
p = doc('#editors-stats .editor-stats-table p:eq(0)')
assert p.text() is None
p = doc('#editors-stats .editor-stats-table p:eq(1)')
assert p.text() is None # Monthly reviews should not be displayed.
p = doc('#editors-stats .editor-stats-table p').eq(0)
assert not p.text()
p = doc('#editors-stats .editor-stats-table p').eq(1)
# Monthly reviews should not be displayed.
assert not p.text()
def test_new_editors(self):
amo.log(amo.LOG.GROUP_USER_ADDED,
@ -530,7 +531,7 @@ class TestHome(EditorTest):
doc = pq(self.client.get(self.url).content)
anchors = doc('#editors-stats .editor-stats-table:eq(2)').find('td a')
anchors = doc('#editors-stats .editor-stats-table').eq(2).find('td a')
assert anchors.eq(0).text() == self.user.display_name
def test_unlisted_queues_only_for_senior_reviewers(self):
@ -586,14 +587,14 @@ class TestHome(EditorTest):
create_addon_file('unlisted 2', '0.1', amo.STATUS_NOMINATED,
amo.STATUS_UNREVIEWED, listed=False)
selector = '.editor-stats-title:eq(0)' # The new addons stats header.
selector = '.editor-stats-title' # The new addons stats header.
self.login_as_senior_editor()
doc = pq(self.client.get(self.url).content)
listed_stats = doc('#editors-stats-charts {0}'.format(selector))
listed_stats = doc('#editors-stats-charts {0}'.format(selector)).eq(0)
assert 'Full Review (1)' in listed_stats.text()
unlisted_stats = doc('#editors-stats-charts-unlisted {0}'.format(
selector))
selector)).eq(0)
assert 'Unlisted Full Reviews (2)' in unlisted_stats.text()
@ -692,7 +693,7 @@ class QueueTest(EditorTest):
def _test_queue_count(self, eq, name, count):
r = self.client.get(self.url)
assert r.status_code == 200
a = pq(r.content)('.tabnav li a:eq(%s)' % eq)
a = pq(r.content)('.tabnav li a').eq(eq)
assert a.text() == '%s (%s)' % (name, count)
assert a.attr('href') == self.url
@ -778,7 +779,7 @@ class TestQueueBasics(QueueTest):
}
for idx, sort in sorts.iteritems():
# Get column link.
a = tr('th:eq(%s)' % idx).find('a')
a = tr('th').eq(idx).find('a')
# Update expected GET parameters with sort type.
params.update(sort=[sort])
# Parse querystring of link to make sure `sort` type is correct.
@ -870,7 +871,7 @@ class TestQueueBasics(QueueTest):
sel = '#editors-stats-charts{0}'.format('' if self.listed
else '-unlisted')
div = doc('{0} .editor-stats-table:eq({1})'.format(sel, eq))
div = doc('{0} .editor-stats-table'.format(sel)).eq(eq)
assert div('.waiting_old').attr('style') == style(widths[0])
assert div('.waiting_med').attr('style') == style(widths[1])
@ -1701,7 +1702,7 @@ class BaseTestQueueSearch(SearchTest):
def test_clear_search_hidden(self):
r = self.search(text_query='admin')
assert r.status_code == 200
assert pq(r.content)('.clear-queue-search').text() is None
assert not pq(r.content)('.clear-queue-search').text()
class TestQueueSearch(BaseTestQueueSearch):
@ -2473,7 +2474,7 @@ class TestReview(ReviewBase):
for qid in queue_ids:
self.addon.update(status=qid)
doc = pq(self.client.get(self.url).content)
assert doc('#breadcrumbs li:eq(1)').text() == text
assert doc('#breadcrumbs li').eq(1).text() == text
def test_viewing(self):
url = reverse('editors.review_viewing')

Просмотреть файл

@ -679,8 +679,7 @@ class TestDeletedThemeLookup(TestCase):
self.login('senior_persona_reviewer@mozilla.com')
r = self.client.get(reverse('editors.themes.deleted'))
assert r.status_code == 200
assert pq(r.content)('tbody td:nth-child(3)').text() == (
self.deleted.name.localized_string)
assert str(self.deleted.name.localized_string) in r.content
def test_perm(self):
self.login('persona_reviewer@mozilla.com')

Просмотреть файл

@ -237,7 +237,7 @@ class FilesBase(object):
self.file_viewer.extract()
res = self.client.get(self.file_url(not_binary))
doc = pq(res.content)
assert doc('#commands td:last').text() == 'Back to review'
assert doc('#commands td')[-1].text_content() == 'Back to review'
def test_files_back_link_anon(self):
self.file_viewer.extract()
@ -246,7 +246,7 @@ class FilesBase(object):
res = self.client.get(self.file_url(not_binary))
assert res.status_code == 200
doc = pq(res.content)
assert doc('#commands td:last').text() == 'Back to addon'
assert doc('#commands td')[-1].text_content() == 'Back to addon'
def test_diff_redirect(self):
ids = self.files[0].id, self.files[1].id
@ -457,7 +457,7 @@ class TestFileViewer(FilesBase, TestCase):
res = self.client.get(self.file_url())
doc = pq(res.content.decode('utf-8'))
assert doc('#id_left option[value=%d]' % f.id).text() == (
assert doc('#id_left option[value="%d"]' % f.id).text() == (
PLATFORM_NAME)
def test_files_for_unlisted_addon_returns_404(self):

Просмотреть файл

@ -845,7 +845,7 @@ class TestGuidSearch(TestCase):
def test_addon_compatibility(self):
addon = Addon.objects.get(id=3615)
r = make_call('search/guid:%s' % addon.guid)
dom = pq(r.content)
dom = pq(r.content, parser='xml')
assert len(dom('addon_compatibility')) == 1
assert dom('addon_compatibility')[0].attrib['id'] == '3615'
assert dom('addon_compatibility')[0].attrib['hosted'] == 'true'
@ -865,7 +865,7 @@ class TestGuidSearch(TestCase):
max_app_version='4')
r = make_call('search/guid:%s' % c.guid)
dom = pq(r.content)
dom = pq(r.content, parser='xml')
assert len(dom('addon_compatibility')) == 1
assert dom('addon_compatibility')[0].attrib['hosted'] == 'false'
assert 'id' not in dom('addon_compatibility')[0].attrib

Просмотреть файл

@ -117,7 +117,7 @@ class SearchBase(ESTestCaseWithAddons):
def check_heading(self):
r = self.client.get(self.url)
assert r.status_code == 200
assert pq(r.content)('.results-count strong').text() is None
assert pq(r.content)('.results-count strong').text() is ''
r = self.client.get(self.url + '&q=ballin')
assert r.status_code == 200

Просмотреть файл

@ -14,6 +14,7 @@ from django.forms.models import model_to_dict
from django.utils.http import urlsafe_base64_encode
from mock import Mock, patch
from pyquery import PyQuery as pq
from olympia import amo
from olympia.amo.tests import TestCase
@ -21,7 +22,6 @@ from olympia.abuse.models import AbuseReport
from olympia.access.models import Group, GroupUser
from olympia.addons.models import Addon, AddonUser, Category
from olympia.amo.helpers import urlparams
from olympia.amo.pyquery_wrapper import PyQuery as pq
from olympia.amo.urlresolvers import reverse
from olympia.bandwagon.models import Collection, CollectionWatcher
from olympia.devhub.models import ActivityLog
@ -250,7 +250,7 @@ class TestEdit(UserViewBase):
doc = pq(self.client.get(self.url).content)
assert doc('input[name=notifications]:checkbox').length == len(choices)
for id, label in choices:
box = doc('input[name=notifications][value=%s]' % id)
box = doc('input[name=notifications][value="%s"]' % id)
if checked:
assert box.filter(':checked').length == 1
else:
@ -979,8 +979,7 @@ class TestRegistration(UserViewBase):
# User doesn't have a confirmation code.
url = reverse('users.confirm', args=[self.user.id, 'code'])
r = self.client.get(url, follow=True)
is_anonymous = pq(r.content)('body').attr('data-anonymous')
assert json.loads(is_anonymous)
assert 'data-anonymous="true"' in r.content
self.user.update(confirmationcode='code')
@ -1152,8 +1151,8 @@ class TestProfileSections(TestCase):
assert doc('.num-addons a[href="#my-submissions"]').length == 1
items = doc('#my-addons .item')
assert items.length == 2
assert items('.install[data-addon=3615]').length == 1
assert items('.install[data-addon=5299]').length == 1
assert items('.install[data-addon="3615"]').length == 1
assert items('.install[data-addon="5299"]').length == 1
def test_my_unlisted_addons(self):
"""I can't see my own unlisted addons on my profile page."""
@ -1171,7 +1170,7 @@ class TestProfileSections(TestCase):
doc = pq(r.content)
items = doc('#my-addons .item')
assert items.length == 1
assert items('.install[data-addon=3615]').length == 1
assert items('.install[data-addon="3615"]').length == 1
def test_not_my_unlisted_addons(self):
"""I can't see others' unlisted addons on their profile pages."""
@ -1190,7 +1189,7 @@ class TestProfileSections(TestCase):
doc = pq(r.content)
items = doc('#my-addons .item')
assert items.length == 1
assert items('.install[data-addon=3615]').length == 1
assert items('.install[data-addon="3615"]').length == 1
def test_my_personas(self):
assert pq(self.client.get(self.url).content)(

Просмотреть файл

@ -692,7 +692,7 @@ class TestFeeds(TestCase):
else 'addons.versions.rss',
args=[slug])
r = self.client.get(url, kwargs, follow=True)
return PyQuery(r.content)
return PyQuery(r.content, parser='xml')
def test_feed_elements_present(self):
"""specific elements are present and reasonably well formed"""