def test_review_tags(self): client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() data.update({'review_tags':['editorial']}) response = client.post(reverse('wiki.new_document'), data) doc = Document.objects.get(slug="a-test-article") combos = ( ([], 0, 0, 0, 0), (['technical',], 1, 1, 0, 0), (['editorial',], 0, 0, 1, 1), (['technical', 'editorial',], 1, 1, 1, 1), ) for tags, a, b, c, d in combos: # Edit the page and set the tags for this test data.update({ 'form': 'rev', 'review_tags': tags }) response = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) response = client.get(reverse('docs.views.docs')) page = pq(response.content) # Check for the section itself, and then the doc eq_(a, page('div#review-technical').length) eq_(b, page("div#review-technical ul li h4 a:contains('%s')" % doc.title).length) eq_(c, page('div#review-editorial').length) eq_(d, page("div#review-editorial ul li h4 a:contains('%s')" % doc.title).length)
class DemoListViewsTest(test_utils.TestCase): fixtures = ['test_users.json'] def setUp(self): self.user, self.admin_user, self.other_user = make_users() self.client = LocalizingClient() def test_all_demos_includes_hidden_for_staff(self): build_submission(self.user) build_hidden_submission(self.user) r = self.client.get(reverse('demos_all')) count = pq(r.content)('h2.count').text() eq_(count, "1 Demo") self.client.login(username=self.admin_user.username, password='******') r = self.client.get(reverse('demos_all')) count = pq(r.content)('h2.count').text() eq_(count, "2 Demos") @attr('bug882709') def test_search_view(self): try: self.client.get(reverse('demos_search')) except: self.fail("Search should not ISE.")
def test_invalid_slugs(self, get_current): """Slugs cannot contain /.""" get_current.return_value.domain = 'testserver' client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() error = 'The slug provided is not valid.' data['slug'] = 'inva/lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, error) data['slug'] = 'no-question-marks?' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, error) data['slug'] = 'no+plus' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, error) data['slug'] = 'valid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects(response, reverse('wiki.document_revisions', args=[data['slug']], locale='en-US'))
def test_raw_section_source(self): """The raw source for a document section can be requested""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) expected = """ <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> """ response = client.get('%s?section=s2&raw=true' % reverse('wiki.document', args=[d.slug])) eq_(normalize_html(expected), normalize_html(response.content))
def test_raw_with_editing_links_source(self): """The raw source for a document can be requested, with section editing links""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) expected = """ <h1 id="s1"><a class="edit-section" data-section-id="s1" data-section-src-url="/en-US/docs/%(slug)s?raw=true&section=s1" href="/en-US/docs/%(slug)s$edit?section=s1&edit_links=true" title="Edit section">Edit</a>Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2"><a class="edit-section" data-section-id="s2" data-section-src-url="/en-US/docs/%(slug)s?raw=true&section=s2" href="/en-US/docs/%(slug)s$edit?section=s2&edit_links=true" title="Edit section">Edit</a>Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3"><a class="edit-section" data-section-id="s3" data-section-src-url="/en-US/docs/%(slug)s?raw=true&section=s3" href="/en-US/docs/%(slug)s$edit?section=s3&edit_links=true" title="Edit section">Edit</a>Head 3</h1> <p>test</p> <p>test</p> """ % { 'slug': d.slug } response = client.get('%s?raw=true&edit_links=true' % reverse('wiki.document', args=[d.slug])) eq_(normalize_html(expected), normalize_html(response.content))
def test_redirect_can_be_clobbered(self): """When an attempt is made to retitle an article, and another article with that title exists but is a redirect, there should be no errors and the redirect should be replaced.""" client = LocalizingClient() client.login(username='******', password='******') exist_title = "Existing doc" exist_slug = "existing-doc" # Create a new doc. data = new_document_data() data.update({"title": exist_title, "slug": exist_slug}) resp = client.post(reverse('wiki.new_document'), data) eq_(302, resp.status_code) # Change title and slug data.update({ 'form': 'rev', 'title': "Changed title", 'slug': "changed-title" }) resp = client.post(reverse('wiki.edit_document', args=[exist_slug]), data) eq_(302, resp.status_code) # Change title and slug back to originals, clobbering the redirect data.update({'form': 'rev', 'title': exist_title, 'slug': exist_slug}) resp = client.post( reverse('wiki.edit_document', args=["changed-title"]), data) eq_(302, resp.status_code)
def test_raw_with_editing_links_source(self): """The raw source for a document can be requested, with section editing links""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) expected = """ <h1 id="s1"><a class="edit-section" data-section-id="s1" data-section-src-url="/en-US/docs/%(slug)s?raw=true&section=s1" href="/en-US/docs/%(slug)s$edit?section=s1&edit_links=true" title="Edit section">Edit</a>Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2"><a class="edit-section" data-section-id="s2" data-section-src-url="/en-US/docs/%(slug)s?raw=true&section=s2" href="/en-US/docs/%(slug)s$edit?section=s2&edit_links=true" title="Edit section">Edit</a>Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3"><a class="edit-section" data-section-id="s3" data-section-src-url="/en-US/docs/%(slug)s?raw=true&section=s3" href="/en-US/docs/%(slug)s$edit?section=s3&edit_links=true" title="Edit section">Edit</a>Head 3</h1> <p>test</p> <p>test</p> """ % {'slug': d.slug} response = client.get('%s?raw=true&edit_links=true' % reverse('wiki.document', args=[d.slug])) eq_(normalize_html(expected), normalize_html(response.content))
def test_invalid_slug(self): """Slugs cannot contain "$", but can contain "/".""" client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() data['title'] = 'valid slug' data['slug'] = 'valid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects(response, reverse('wiki.document', args=[data['slug']], locale='en-US')) # Slashes should be fine data['title'] = 'valid with slash' data['slug'] = 'va/lid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects(response, reverse('wiki.document', args=[data['slug']], locale='en-US')) # Dollar sign is reserved for verbs data['title'] = 'invalid with dollars' data['slug'] = 'inva$lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.') # Question mark is reserved for query params data['title'] = 'invalid with questions' data['slug'] = 'inva?lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.')
def test_invalid_reserved_term_slug(self): """Slugs should not collide with reserved URL patterns""" client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() # TODO: This is info derived from urls.py, but unsure how to DRY it reserved_slugs = ( 'ckeditor_config.js', 'watch-ready-for-review', 'unwatch-ready-for-review', 'watch-approved', 'unwatch-approved', '.json', 'new', 'all', 'preview-wiki-content', 'category/10', 'needs-review/technical', 'needs-review/', 'feeds/atom/all/', 'feeds/atom/needs-review/technical', 'feeds/atom/needs-review/', 'tag/tasty-pie' ) for term in reserved_slugs: data['title'] = 'invalid with %s' % term data['slug'] = term response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.')
def test_title_slug_collision_errors(self): """When an attempt is made to retitle an article and another with that title already exists, there should be form errors""" client = LocalizingClient() client.login(username='******', password='******') exist_title = "Existing doc" exist_slug = "existing-doc" # Create a new doc. data = new_document_data() data.update({ "title": exist_title, "slug": exist_slug }) resp = client.post(reverse('wiki.new_document'), data) eq_(302, resp.status_code) # Create another new doc. data = new_document_data() data.update({ "title": 'Some new title', "slug": 'some-new-title' }) response = client.post(reverse('wiki.new_document'), data) eq_(302, resp.status_code) # Now, post an update with duplicate slug and title data.update({ 'form': 'rev', 'title': exist_title, 'slug': exist_slug }) resp = client.post(reverse('wiki.edit_document', args=['some-new-title']), data) eq_(200, resp.status_code) p = pq(resp.content) ok_(p.find('.errorlist').length > 0) ok_(p.find('.errorlist a[href="#id_title"]').length > 0) ok_(p.find('.errorlist a[href="#id_slug"]').length > 0)
def test_redirect_can_be_clobbered(self): """When an attempt is made to retitle an article, and another article with that title exists but is a redirect, there should be no errors and the redirect should be replaced.""" client = LocalizingClient() client.login(username='******', password='******') exist_title = "Existing doc" exist_slug = "existing-doc" # Create a new doc. data = new_document_data() data.update({ "title": exist_title, "slug": exist_slug }) resp = client.post(reverse('wiki.new_document'), data) eq_(302, resp.status_code) # Change title and slug data.update({'form': 'rev', 'title': "Changed title", 'slug': "changed-title"}) resp = client.post(reverse('wiki.edit_document', args=[exist_slug]), data) eq_(302, resp.status_code) # Change title and slug back to originals, clobbering the redirect data.update({'form': 'rev', 'title': exist_title, 'slug': exist_slug}) resp = client.post(reverse('wiki.edit_document', args=["changed-title"]), data) eq_(302, resp.status_code)
def test_title_slug_collision_errors(self): """When an attempt is made to retitle an article and another with that title already exists, there should be form errors""" client = LocalizingClient() client.login(username='******', password='******') exist_title = "Existing doc" exist_slug = "existing-doc" # Create a new doc. data = new_document_data() data.update({"title": exist_title, "slug": exist_slug}) resp = client.post(reverse('wiki.new_document'), data) eq_(302, resp.status_code) # Create another new doc. data = new_document_data() data.update({"title": 'Some new title', "slug": 'some-new-title'}) response = client.post(reverse('wiki.new_document'), data) eq_(302, resp.status_code) # Now, post an update with duplicate slug and title data.update({'form': 'rev', 'title': exist_title, 'slug': exist_slug}) resp = client.post( reverse('wiki.edit_document', args=['some-new-title']), data) eq_(200, resp.status_code) p = pq(resp.content) ok_(p.find('.errorlist').length > 0) ok_(p.find('.errorlist a[href="#id_title"]').length > 0) ok_(p.find('.errorlist a[href="#id_slug"]').length > 0)
def test_invalid_slug(self): """Slugs cannot contain "$", but can contain "/".""" client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() data['title'] = 'valid slug' data['slug'] = 'valid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects(response, reverse('wiki.document', args=[data['slug']])) # Slashes should be fine data['title'] = 'valid with slash' data['slug'] = 'va/lid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects(response, reverse('wiki.document', args=[data['slug']])) # Dollar sign is reserved for verbs data['title'] = 'invalid with dollars' data['slug'] = 'inva$lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.') # Question mark is reserved for query params data['title'] = 'invalid with questions' data['slug'] = 'inva?lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.')
def test_raw_section_source(self): """The raw source for a document section can be requested""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) expected = """ <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> """ response = client.get('%s?section=s2&raw=true' % reverse('wiki.document', args=[d.slug])) eq_(normalize_html(expected), normalize_html(response.content))
def test_kumawiki_waffle_flag(self): # Turn off the new wiki for everyone self.kumawiki_flag.everyone = False self.kumawiki_flag.save() client = LocalizingClient() resp = client.get(reverse('wiki.all_documents')) eq_(404, resp.status_code) resp = client.get(reverse('docs')) page = pq(resp.content) eq_(0, page.find('#kumawiki_preview').length) client.login(username='******', password='******') # Turn on the wiki for just superusers, ignore everyone else self.kumawiki_flag.superusers = True self.kumawiki_flag.everyone = None self.kumawiki_flag.save() resp = client.get(reverse('wiki.all_documents')) eq_(200, resp.status_code) resp = client.get(reverse('docs')) page = pq(resp.content) eq_(1, page.find('#kumawiki_preview').length)
class UploadImageTestCase(TestCase): fixtures = ['users.json', 'questions.json'] def setUp(self): super(UploadImageTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username='******', password='******') def tearDown(self): ImageAttachment.objects.all().delete() super(UploadImageTestCase, self).tearDown() def test_model_invalid(self): """Specifying an invalid model returns 400.""" r = post(self.client, 'upload.up_image_async', {'image': ''}, args=['invalid.model', 123]) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Model does not exist.', json_r['message']) def test_object_notexist(self): """Upload nothing returns 404 error and html content.""" r = post(self.client, 'upload.up_image_async', {'image': ''}, args=['questions.Question', 123]) eq_(404, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Object does not exist.', json_r['message']) def test_empty_image(self): """Upload nothing returns 400 error and json content.""" r = post(self.client, 'upload.up_image_async', {'image': ''}, args=['questions.Question', 1]) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Invalid or no image received.', json_r['message']) def test_basic(self): """Uploading an image works.""" # TODO: posting a valid image through the test client uploads it raise SkipTest def test_invalid_image(self): """Make sure invalid files are not accepted as images.""" f = open('apps/upload/__init__.py', 'rb') r = post(self.client, 'upload.up_image_async', {'image': f}, args=['questions.Question', 1]) f.close() eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Invalid or no image received.', json_r['message'])
class GalleryUploadTestCase(TestCase): fixtures = ['users.json'] def setUp(self): super(GalleryUploadTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username='******', password='******') self.u = User.objects.get(username='******') def tearDown(self): Image.objects.all().delete() Video.objects.all().delete() super(GalleryUploadTestCase, self).tearDown() def test_image_draft_shows(self): """The image draft is loaded for this user.""" image(title=get_draft_title(self.u), creator=self.u) response = get(self.client, 'gallery.gallery', args=['image']) eq_(200, response.status_code) doc = pq(response.content) assert doc('.image-preview img').attr('src').endswith('098f6b.jpg') eq_(1, doc('.image-preview img').length) def test_video_draft_shows(self): """The video draft is loaded for this user.""" video(title=get_draft_title(self.u), creator=self.u) response = get(self.client, 'gallery.gallery', args=['image']) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_(3, doc('ul li.video-preview').length) def test_image_draft_post(self): """Posting to the page saves the field values for the image draft.""" image(title=get_draft_title(self.u), creator=self.u) response = post(self.client, 'gallery.gallery', { 'description': '??', 'title': 'test' }, args=['image']) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_('??', doc('#gallery-upload-modal textarea').html()) eq_('test', doc('#gallery-upload-modal input[name="title"]').val()) def test_video_draft_post(self): """Posting to the page saves the field values for the video draft.""" video(title=get_draft_title(self.u), creator=self.u) response = post(self.client, 'gallery.gallery', {'title': 'zTestz'}, args=['image']) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_('zTestz', doc('#gallery-upload-modal input[name="title"]').val())
def test_retitling_ignored_for_iframe(self): """When the title of an article is edited in an iframe, the change is ignored.""" client = LocalizingClient() client.login(username='******', password='******') new_title = 'Some New Title' d, r = doc_rev() old_title = d.title data = new_document_data() data.update({'title': new_title, 'slug': d.slug, 'form': 'rev'}) client.post( '%s?iframe=1' % reverse('wiki.edit_document', args=[d.slug]), data) eq_(old_title, Document.uncached.get(slug=d.slug).title) assert "REDIRECT" not in Document.uncached.get(title=old_title).html
class GalleryUploadTestCase(TestCase): fixtures = ["users.json"] def setUp(self): super(GalleryUploadTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username="******", password="******") self.u = User.objects.get(username="******") def tearDown(self): Image.objects.all().delete() Video.objects.all().delete() super(GalleryUploadTestCase, self).tearDown() def test_image_draft_shows(self): """The image draft is loaded for this user.""" image(title=get_draft_title(self.u), creator=self.u) response = get(self.client, "gallery.gallery", args=["image"]) eq_(200, response.status_code) doc = pq(response.content) assert doc(".image-preview img").attr("src").endswith("098f6b.jpg") eq_(1, doc(".image-preview img").length) def test_video_draft_shows(self): """The video draft is loaded for this user.""" video(title=get_draft_title(self.u), creator=self.u) response = get(self.client, "gallery.gallery", args=["image"]) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_(3, doc("ul li.video-preview").length) def test_image_draft_post(self): """Posting to the page saves the field values for the image draft.""" image(title=get_draft_title(self.u), creator=self.u) response = post(self.client, "gallery.gallery", {"description": "??", "title": "test"}, args=["image"]) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_("??", doc("#gallery-upload-modal textarea").html()) eq_("test", doc('#gallery-upload-modal input[name="title"]').val()) def test_video_draft_post(self): """Posting to the page saves the field values for the video draft.""" video(title=get_draft_title(self.u), creator=self.u) response = post(self.client, "gallery.gallery", {"title": "zTestz"}, args=["image"]) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_("zTestz", doc('#gallery-upload-modal input[name="title"]').val())
def test_invalid_slug(self): """Slugs cannot contain /.""" client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() data['slug'] = 'inva/lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.') data['slug'] = 'valid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects(response, reverse('wiki.document_revisions', args=[data['slug']], locale='en-US'))
def test_changing_products(self): """Changing products works as expected.""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev() data = new_document_data() data.update({'products': ['desktop', 'sync'], 'form': 'doc'}) client.post(reverse('wiki.edit_document', args=[d.slug]), data) tags_eq(d, ['desktop', 'sync']) data.update({'products': ['mobile'], 'form': 'doc'}) client.post(reverse('wiki.edit_document', args=[data['slug']]), data) tags_eq(d, ['mobile'])
def test_retitling(self): """When the title of an article is edited, a redirect is made.""" # Not testing slug changes separately; the model tests cover those plus # slug+title changes. If title changes work in the view, the rest # should also. client = LocalizingClient() client.login(username='******', password='******') new_title = 'Some New Title' d, r = doc_rev() old_title = d.title data = new_document_data() data.update({'title': new_title, 'slug': d.slug, 'form': 'rev'}) client.post(reverse('wiki.edit_document', args=[d.slug]), data) eq_(new_title, Document.uncached.get(slug=d.slug).title) assert "REDIRECT" in Document.uncached.get(title=old_title).html
def test_retitling_ignored_for_iframe(self): """When the title of an article is edited in an iframe, the change is ignored.""" client = LocalizingClient() client.login(username='******', password='******') new_title = 'Some New Title' d, r = doc_rev() old_title = d.title data = new_document_data() data.update({'title': new_title, 'slug': d.slug, 'form': 'rev'}) client.post('%s?iframe=1' % reverse('wiki.edit_document', args=[d.slug]), data) eq_(old_title, Document.uncached.get(slug=d.slug).title) assert "REDIRECT" not in Document.uncached.get(title=old_title).html
def test_raw_section_edit(self): client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) replace = """ <h1 id="s2">Replace</h1> <p>replace</p> """ expected = """ <h1 id="s2">Replace</h1> <p>replace</p> """ response = client.post('%s?section=s2&raw=true' % reverse('wiki.edit_document', args=[d.slug]), {"form": "rev", "content": replace}, follow=True) eq_(normalize_html(expected), normalize_html(response.content)) expected = """ <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Replace</h1> <p>replace</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """ response = client.get('%s?raw=true' % reverse('wiki.document', args=[d.slug])) eq_(normalize_html(expected), normalize_html(response.content))
def test_raw_section_edit(self): client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) replace = """ <h1 id="s2">Replace</h1> <p>replace</p> """ expected = """ <h1 id="s2">Replace</h1> <p>replace</p> """ response = client.post('%s?section=s2&raw=true' % reverse('wiki.edit_document', args=[d.slug]), { "form": "rev", "content": replace }, follow=True) eq_(normalize_html(expected), normalize_html(response.content)) expected = """ <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Replace</h1> <p>replace</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """ response = client.get('%s?raw=true' % reverse('wiki.document', args=[d.slug])) eq_(normalize_html(expected), normalize_html(response.content))
def test_ban_middleware(self): """Ban middleware functions correctly.""" client = LocalizingClient() client.login(username='******', password='******') resp = client.get('/') self.assertTemplateNotUsed(resp, 'users/user_banned.html') admin = User.objects.get(username='******') testuser = User.objects.get(username='******') ban = UserBan(user=testuser, by=admin, reason='Banned by unit test.', is_active=True) ban.save() resp = client.get('/') self.assertTemplateUsed(resp, 'users/user_banned.html')
def test_invalid_slug(self, get_current): """Slugs cannot contain /.""" get_current.return_value.domain = 'testserver' client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() data['slug'] = 'inva/lid' response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.') data['slug'] = 'valid' response = client.post(reverse('wiki.new_document'), data) self.assertRedirects( response, reverse('wiki.document_revisions', args=[data['slug']], locale='en-US'))
def test_retitling(self): """When the title of an article is edited, a redirect is made.""" # Not testing slug changes separately; the model tests cover those plus # slug+title changes. If title changes work in the view, the rest # should also. client = LocalizingClient() client.login(username='******', password='******') new_title = 'Some New Title' d, r = doc_rev() old_title = d.title data = new_document_data() data.update({'title': new_title, 'slug': d.slug, 'form': 'doc'}) client.post(reverse('wiki.edit_document', args=[d.slug]), data) eq_(new_title, Document.uncached.get(slug=d.slug).title) assert Document.uncached.get(title=old_title).redirect_url()
def test_changing_metadata(self): """Changing metadata works as expected.""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev() data = new_document_data() data.update({'firefox_versions': [1, 2, 3], 'operating_systems': [1, 3], 'form': 'doc'}) client.post(reverse('wiki.edit_document', args=[d.slug]), data) eq_(3, d.firefox_versions.count()) eq_(2, d.operating_systems.count()) data.update({'firefox_versions': [1, 2], 'operating_systems': [2], 'form': 'doc'}) client.post(reverse('wiki.edit_document', args=[data['slug']]), data) eq_(2, d.firefox_versions.count()) eq_(1, d.operating_systems.count())
def test_ban_middleware(self): """Ban middleware functions correctly.""" client = LocalizingClient() client.login(username='******', password='******') resp = client.get('/') self.assertTemplateNotUsed(resp, 'users/user_banned.html') admin = User.objects.get(username='******') testuser = User.objects.get(username='******') ban = UserBan(user=testuser, by=admin, reason='Banned by unit test.', is_active=True) ban.save() resp = client.get('/') self.assertTemplateUsed(resp, 'users/user_banned.html')
def test_ban_permission(self): """The ban permission controls access to the ban view.""" client = LocalizingClient() admin = User.objects.get(username="******") testuser = User.objects.get(username="******") # testuser doesn't have ban permission, can't ban. client.login(username="******", password="******") ban_url = reverse("users.ban_user", kwargs={"user_id": admin.id}) resp = client.get(ban_url) eq_(302, resp.status_code) ok_(settings.LOGIN_URL in resp["Location"]) client.logout() # admin has ban permission, can ban. client.login(username="******", password="******") ban_url = reverse("users.ban_user", kwargs={"user_id": testuser.id}) resp = client.get(ban_url) eq_(200, resp.status_code)
def test_ban_view(self): testuser = User.objects.get(username="******") admin = User.objects.get(username="******") client = LocalizingClient() client.login(username="******", password="******") data = {"reason": "Banned by unit test."} ban_url = reverse("users.ban_user", kwargs={"user_id": testuser.id}) resp = client.post(ban_url, data) eq_(302, resp.status_code) ok_(testuser.get_absolute_url() in resp["Location"]) testuser_banned = User.objects.get(username="******") ok_(not testuser_banned.is_active) bans = UserBan.objects.filter(user=testuser, by=admin, reason="Banned by unit test.") ok_(bans.count())
def test_ban_permission(self): """The ban permission controls access to the ban view.""" client = LocalizingClient() admin = User.objects.get(username='******') testuser = User.objects.get(username='******') # testuser doesn't have ban permission, can't ban. client.login(username='******', password='******') ban_url = reverse('users.ban_user', kwargs={'user_id': admin.id}) resp = client.get(ban_url) eq_(302, resp.status_code) ok_(settings.LOGIN_URL in resp['Location']) client.logout() # admin has ban permission, can ban. client.login(username='******', password='******') ban_url = reverse('users.ban_user', kwargs={'user_id': testuser.id}) resp = client.get(ban_url) eq_(200, resp.status_code)
def test_edit_midair_collision(self): client = LocalizingClient() client.login(username='******', password='******') # Post a new document. data = new_document_data() resp = client.post(reverse('wiki.new_document'), data) doc = Document.objects.get(slug=data['slug']) # Edit #1 starts... resp = client.get(reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id1 = page.find('input[name="current_rev"]').attr('value') # Edit #2 starts... resp = client.get(reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id2 = page.find('input[name="current_rev"]').attr('value') # Edit #2 submits successfully data.update({ 'form': 'rev', 'content': 'This edit got there first', 'current_rev': rev_id2 }) resp = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) eq_(302, resp.status_code) # Edit #1 submits, but receives a mid-aired notification data.update({ 'form': 'rev', 'content': 'This edit gets mid-aired', 'current_rev': rev_id1 }) resp = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) eq_(200, resp.status_code) ok_( unicode(MIDAIR_COLLISION).encode('utf-8') in resp.content, "Midair collision message should appear")
def test_invalid_reserved_term_slug(self): """Slugs should not collide with reserved URL patterns""" client = LocalizingClient() client.login(username='******', password='******') data = new_document_data() # TODO: This is info derived from urls.py, but unsure how to DRY it reserved_slugs = ('ckeditor_config.js', 'watch-ready-for-review', 'unwatch-ready-for-review', 'watch-approved', 'unwatch-approved', '.json', 'new', 'all', 'preview-wiki-content', 'category/10', 'needs-review/technical', 'needs-review/', 'feeds/atom/all/', 'feeds/atom/needs-review/technical', 'feeds/atom/needs-review/', 'tag/tasty-pie') for term in reserved_slugs: data['title'] = 'invalid with %s' % term data['slug'] = term response = client.post(reverse('wiki.new_document'), data) self.assertContains(response, 'The slug provided is not valid.')
def test_ban_view(self): testuser = User.objects.get(username='******') admin = User.objects.get(username='******') client = LocalizingClient() client.login(username='******', password='******') data = {'reason': 'Banned by unit test.'} ban_url = reverse('users.ban_user', kwargs={'user_id': testuser.id}) resp = client.post(ban_url, data) eq_(302, resp.status_code) ok_(testuser.get_absolute_url() in resp['Location']) testuser_banned = User.objects.get(username='******') ok_(not testuser_banned.is_active) bans = UserBan.objects.filter(user=testuser, by=admin, reason='Banned by unit test.') ok_(bans.count())
def test_changing_metadata(self): """Changing metadata works as expected.""" client = LocalizingClient() client.login(username='******', password='******') d, r = doc_rev() data = new_document_data() data.update({ 'firefox_versions': [1, 2, 3], 'operating_systems': [1, 3], 'form': 'doc' }) client.post(reverse('wiki.edit_document', args=[d.slug]), data) eq_(3, d.firefox_versions.count()) eq_(2, d.operating_systems.count()) data.update({ 'firefox_versions': [1, 2], 'operating_systems': [2], 'form': 'doc' }) client.post(reverse('wiki.edit_document', args=[data['slug']]), data) eq_(2, d.firefox_versions.count()) eq_(1, d.operating_systems.count())
def test_edit_midair_collision(self): client = LocalizingClient() client.login(username='******', password='******') # Post a new document. data = new_document_data() resp = client.post(reverse('wiki.new_document'), data) doc = Document.objects.get(slug=data['slug']) # Edit #1 starts... resp = client.get(reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id1 = page.find('input[name="current_rev"]').attr('value') # Edit #2 starts... resp = client.get(reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id2 = page.find('input[name="current_rev"]').attr('value') # Edit #2 submits successfully data.update({ 'form': 'rev', 'content': 'This edit got there first', 'current_rev': rev_id2 }) resp = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) eq_(302, resp.status_code) # Edit #1 submits, but receives a mid-aired notification data.update({ 'form': 'rev', 'content': 'This edit gets mid-aired', 'current_rev': rev_id1 }) resp = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) eq_(200, resp.status_code) ok_(unicode(MIDAIR_COLLISION).encode('utf-8') in resp.content, "Midair collision message should appear")
def test_ban_view(self): testuser = User.objects.get(username='******') admin = User.objects.get(username='******') client = LocalizingClient() client.login(username='******', password='******') data = {'reason': 'Banned by unit test.'} ban_url = reverse('users.ban_user', kwargs={'user_id': testuser.id}) resp = client.post(ban_url, data) eq_(302, resp.status_code) ok_(testuser.get_absolute_url() in resp['Location']) testuser_banned = User.objects.get(username='******') ok_(not testuser_banned.is_active) bans = UserBan.objects.filter(user=testuser, by=admin, reason='Banned by unit test.') ok_(bans.count())
class ChangeEmailTestCase(TestCase): fixtures = ['users.json'] def setUp(self): self.client = LocalizingClient() # Create some notifications EventWatch.objects.create(content_type_id=1, watch_id=2, email='user47963@nowhere') @mock.patch_object(Site.objects, 'get_current') def test_user_change_email(self, get_current): """Send email to change user's email and then change it.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') # Attempt to change email. response = self.client.post(reverse('users.change_email'), {'email': '*****@*****.**'}, follow=True) eq_(200, response.status_code) # Be notified to click a confirmation link. eq_(1, len(mail.outbox)) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] assert ec.activation_key in mail.outbox[0].body eq_('*****@*****.**', ec.email) # Visit confirmation link to change email. response = self.client.get(reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) u = User.objects.get(username='******') eq_('*****@*****.**', u.email) # Notifications have been updated. # TODO: remove this after notifications model is updated. ew = EventWatch.objects.get() eq_('*****@*****.**', ew.email)
class UploadImageTestCase(TestCase): fixtures = ['users.json'] def setUp(self): super(UploadImageTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username='******', password='******') def tearDown(self): Image.objects.all().delete() super(UploadImageTestCase, self).tearDown() def test_empty_image(self): """Specifying an invalid model returns 400.""" r = post(self.client, 'gallery.upload_async', {'file': ''}, args=['image']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your image.', json_r['message']) eq_('You have not selected an image to upload.', json_r['errors']['file'][0]) def test_upload_image(self): """Uploading an image works.""" with open(TEST_IMG) as f: r = post(self.client, 'gallery.upload_async', {'file': f}, args=['image']) img = Image.objects.all()[0] eq_(1, Image.objects.count()) eq_(200, r.status_code) json_r = json.loads(r.content) eq_('success', json_r['status']) file = json_r['file'] eq_('test.jpg', file['name']) eq_(90, file['width']) eq_(120, file['height']) assert file['url'].endswith(img.get_absolute_url()) eq_('pcraciunoiu', img.creator.username) eq_(150, img.file.width) eq_(200, img.file.height) eq_(get_draft_title(img.creator), img.title) eq_('Autosaved draft.', img.description) eq_('en-US', img.locale) def test_upload_unicode_image(self): """Uploading an unicode image works.""" with open(u'apps/upload/tests/media/123ascii\u6709\u52b9.jpg') as f: r = post(self.client, 'gallery.upload_async', {'file': f}, args=['image']) eq_(1, Image.objects.count()) eq_(200, r.status_code) json_r = json.loads(r.content) eq_('success', json_r['status']) def test_invalid_image(self): """Make sure invalid files are not accepted as images.""" with open('apps/gallery/__init__.py', 'rb') as f: r = post(self.client, 'gallery.upload_async', {'file': f}, args=['image']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your image.', json_r['message']) eq_('Upload a valid image. The file you uploaded was either not an ' 'image or a corrupted image.', json_r['errors']['file'][0]) def test_invalid_image_extension(self): """Make sure invalid extensions are not accepted as images.""" with open('apps/upload/tests/media/test_invalid.ext', 'rb') as f: r = post(self.client, 'gallery.upload_async', {'file': f}, args=['image']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your image.', json_r['message']) eq_('Please upload an image with one of the following extensions: ' 'jpg, jpeg, png, gif.', json_r['errors']['__all__'][0]) def test_invalid_thumbnail_extension(self): """Make sure invalid extensions are not accepted as thumbnails.""" with open('apps/upload/tests/media/test_invalid.ext', 'rb') as f: r = post(self.client, 'gallery.upload_async', {'thumbnail': f}, args=['video']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your video.', json_r['message']) eq_('Please upload an image with one of the following extensions: ' 'jpg, jpeg, png, gif.', json_r['errors']['__all__'][0]) def test_upload_image_long_filename(self): """Uploading an image with a filename that's too long fails.""" with open('apps/upload/tests/media/a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_yes_.jpg')\ as f: r = post(self.client, 'gallery.upload_async', {'file': f}, args=['image']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your image.', json_r['message']) eq_(forms.MSG_IMAGE_LONG % {'length': 251, 'max': settings.MAX_FILENAME_LENGTH}, json_r['errors']['file'][0]) def test_upload_draft_image(self): """Uploading draft image works, sets locale too.""" u = User.objects.get(username='******') img = image(creator=u, title=get_draft_title(u)) # No thumbnail yet. eq_(None, img.thumbnail) r = post(self.client, 'gallery.upload', {'locale': 'de', 'title': 'Hasta la vista', 'description': 'Auf wiedersehen!'}, args=['image']) eq_(200, r.status_code) img = Image.objects.all()[0] eq_('de', img.locale) eq_('Hasta la vista', img.title) eq_('Auf wiedersehen!', img.description) # Thumbnail generated after form is saved. eq_(90, img.thumbnail.width) def test_image_title_locale_unique_validation(self): """Posting an existing locale/title combination shows a validation error.""" u = User.objects.get(username='******') image(creator=u, title=get_draft_title(u)) post(self.client, 'gallery.upload', {'locale': 'de', 'title': 'Hasta la vista', 'description': 'Auf wiedersehen!'}, args=['image']) image(creator=u, title=get_draft_title(u)) r = post(self.client, 'gallery.upload', {'locale': 'de', 'title': 'Hasta la vista', 'description': 'Auf wiedersehen!'}, args=['image']) eq_(200, r.status_code) doc = pq(r.content) msg = 'Image with this Locale and Title already exists.' assert doc('ul.errorlist li').text().startswith(msg)
class BrowserIDTestCase(TestCase): fixtures = ['test_users.json'] def setUp(self): # Ensure @ssl_required goes unenforced. settings.DEBUG = True # Set up some easily-testable redirects. settings.LOGIN_REDIRECT_URL = 'SUCCESS' settings.LOGIN_REDIRECT_URL_FAILURE = 'FAILURE' # BrowserID will squawk if this isn't set settings.SITE_URL = 'http://testserver' self.client = LocalizingClient() def test_invalid_post(self): resp = self.client.post( reverse('users.browserid_verify', locale='en-US')) eq_(302, resp.status_code) ok_('FAILURE' in resp['Location']) @mock.patch('users.views._verify_browserid') def test_invalid_assertion(self, _verify_browserid): _verify_browserid.return_value = None resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'bad data'}) eq_(302, resp.status_code) ok_('FAILURE' in resp['Location']) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_django_user(self, _verify_browserid): _verify_browserid.return_value = {'email': '*****@*****.**'} # Posting the fake assertion to browserid_verify should work, with the # actual verification method mocked out. resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('SUCCESS' in resp['Location']) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_explain_popup(self, _verify_browserid): _verify_browserid.return_value = {'email': '*****@*****.**'} resp = self.client.get(reverse('home', locale='en-US')) # Posting the fake assertion to browserid_verify should work, with the # actual verification method mocked out. resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_('1', resp.cookies.get('browserid_explained').value) resp = self.client.get(reverse('users.logout'), locale='en-US') # even after logout, cookie should prevent the toggle resp = self.client.get(reverse('home', locale='en-US')) eq_('1', self.client.cookies.get('browserid_explained').value) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_mindtouch_user(self, _verify_browserid): if not settings.DEKIWIKI_ENDPOINT: # Don't even bother with this test, if there's no MindTouch API raise SkipTest() mt_email = '*****@*****.**' _verify_browserid.return_value = {'email': mt_email} # Probably overkill but let's be sure we're testing the right thing. try: User.objects.get(email=mt_email) ok_(False, "The MindTouch user shouldn't exist in Django yet.") except User.DoesNotExist: pass if not getattr(settings, 'DEKIWIKI_MOCK', False): # HACK: Ensure that expected user details are in MindTouch when not # mocking the API user_xml = MINDTOUCH_USER_XML % dict(username="******", email=mt_email, fullname="None", status="active", language="", timezone="-08:00", role="Contributor") DekiUserBackend.put_mindtouch_user(deki_user_id='=testaccount', user_xml=user_xml) deki_user = DekiUserBackend.get_deki_user_by_email(mt_email) ok_(deki_user is not None, "The MindTouch user should exist") # Posting the fake assertion to browserid_verify should work, with the # actual verification method mocked out. resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('SUCCESS' in resp['Location']) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) # And, after all the above, there should be a Django user now. try: User.objects.get(email=mt_email) except User.DoesNotExist: ok_(False, "The MindTouch user should exist in Django now.") @attr('current') @mock.patch('dekicompat.backends.DekiUserBackend.get_deki_user_by_email') @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_mt_disabled(self, _verify_browserid, mock_get_deki_user_by_email): """On valid browserid assertion, when Django user is not found, yet MindTouch API disabled, there should be no attempt to look the user up in MindTouch""" mt_email = '*****@*****.**' _verify_browserid.return_value = {'email': mt_email} # HACK: mock has an assert_called_with, but I want something like # never_called or call_count. Instead, I have this: trap = {'was_called': False} def my_get_deki_user_by_email(email): trap['was_called'] = True return False mock_get_deki_user_by_email.side_effect = my_get_deki_user_by_email _old = settings.DEKIWIKI_ENDPOINT settings.DEKIWIKI_ENDPOINT = False resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) settings.DEKIWIKI_ENDPOINT = _old # This should end up being a redirect to register eq_(302, resp.status_code) ok_('browserid_register' in resp['Location']) ok_(not trap['was_called']) @mock_missing_get_deki_user_by_email @mock_missing_get_deki_user @mock_post_mindtouch_user @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_new_account_creation(self, _verify_browserid): new_username = '******' new_email = '*****@*****.**' _verify_browserid.return_value = {'email': new_email} try: user = User.objects.get(email=new_email) ok_(False, "User for email should not yet exist") except User.DoesNotExist: pass if not getattr(settings, 'DEKIWIKI_MOCK', False): # HACK: When not mocking the MindTouch API, ensure that there's no # leftover user with the same name & email as what we want to # register import random rand_num = random.randint(0, 1000000) user_xml = MINDTOUCH_USER_XML % dict(username="******" % (new_username, rand_num), email="%s_%s" % (rand_num, new_email), fullname="", status="inactive", language="", timezone="-08:00", role="Contributor") DekiUserBackend.put_mindtouch_user(deki_user_id='=%s' % new_username, user_xml=user_xml) # Sign in with a verified email, but with no existing account resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) # This should be a redirect to the BrowserID registration page. redir_url = resp['Location'] reg_url = reverse('users.browserid_register', locale='en-US') ok_(reg_url in redir_url) # And, as part of the redirect, the verified email address should be in # our session now. ok_(SESSION_VERIFIED_EMAIL in self.client.session.keys()) verified_email = self.client.session[SESSION_VERIFIED_EMAIL] eq_(new_email, verified_email) # Grab the redirect, assert that there's a create_user form present resp = self.client.get(redir_url) page = pq(resp.content) form = page.find('form#create_user') eq_(1, form.length) # There should be no error lists on first load eq_(0, page.find('.errorlist').length) # Submit the create_user form, with a chosen username resp = self.client.post(redir_url, { 'username': '******', 'action': 'register' }) # The submission should result in a redirect to the session's redirect # value eq_(302, resp.status_code) redir_url = resp['Location'] ok_('SUCCESS' in redir_url) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) if settings.DEKIWIKI_ENDPOINT: ok_(self.client.cookies.get('authtoken'), 'Should have set ' 'authtoken cookie for ' 'MindTouch') # Ensure that the user was created, and with the submitted username and # verified email address try: user = User.objects.get(email=new_email) eq_(new_username, user.username) eq_(new_email, user.email) except User.DoesNotExist: ok_(False, "New user should have been created") @mock_missing_get_deki_user_by_email @mock_post_mindtouch_user @mock_put_mindtouch_user @mock_mindtouch_login @mock_get_deki_user @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_existing_account_login( self, _verify_browserid): """ Removed the existing user form: we don't auth the password with MindTouch anymore """ new_email = '*****@*****.**' _verify_browserid.return_value = {'email': new_email} try: User.objects.get(email=new_email) ok_(False, "User for email should not yet exist") except User.DoesNotExist: pass # Sign in with a verified email, but with no existing account resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) # This should be a redirect to the BrowserID registration page. redir_url = resp['Location'] reg_url = reverse('users.browserid_register', locale='en-US') ok_(reg_url in redir_url) # And, as part of the redirect, the verified email address should be in # our session now. ok_(SESSION_VERIFIED_EMAIL in self.client.session.keys()) verified_email = self.client.session[SESSION_VERIFIED_EMAIL] eq_(new_email, verified_email) # Grab the redirect, assert that there's a create_user form present resp = self.client.get(redir_url) page = pq(resp.content) form = page.find('form#existing_user') eq_(0, form.length) @mock.patch('dekicompat.backends.DekiUserBackend.mindtouch_login') @mock.patch('users.views._verify_browserid') def test_mindtouch_disabled_redirect_login(self, _verify_browserid, mock_mindtouch_login): """When DEKIWIKI_ENDPOINT unavailable, skip MindTouch auth.""" _verify_browserid.return_value = {'email': '*****@*****.**'} # HACK: mock has an assert_called_with, but I want something like # never_called or call_count. Instead, I have this: trap = {'was_called': False} def my_mindtouch_login(username, password, force=False): trap['was_called'] = True return False mock_mindtouch_login.side_effect = my_mindtouch_login _old = settings.DEKIWIKI_ENDPOINT settings.DEKIWIKI_ENDPOINT = False resp = self.client.post( reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) settings.DEKIWIKI_ENDPOINT = _old eq_(302, resp.status_code) ok_('SUCCESS' in resp['Location']) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) ok_(not trap['was_called']) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_changing_email(self, _verify_browserid): # just need to be authenticated, not necessarily BrowserID self.client.login(username='******', password='******') _verify_browserid.return_value = {'email': '*****@*****.**'} resp = self.client.post( reverse('users.browserid_change_email', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('profiles/testuser/edit' in resp['Location']) resp = self.client.get( reverse('devmo_profile_edit', locale='en-US', args=[ 'testuser', ])) eq_(200, resp.status_code) doc = pq(resp.content) ok_('*****@*****.**' in doc.find('li#field_email').text()) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_doesnt_steal_email(self, _verify_browserid): # just need to be authenticated, not necessarily BrowserID self.client.login(username='******', password='******') _verify_browserid.return_value = {'email': '*****@*****.**'} # doesn't change email if the new email already belongs to another user resp = self.client.post( reverse('users.browserid_change_email', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('change_email' in resp['Location']) resp = self.client.get( reverse('devmo_profile_edit', locale='en-US', args=[ 'testuser', ])) eq_(200, resp.status_code) doc = pq(resp.content) ok_('*****@*****.**' in doc.find('li#field_email').text())
class DeleteEditImageTestCase(TestCase): fixtures = ['users.json'] def setUp(self): super(DeleteEditImageTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username='******', password='******') def tearDown(self): Image.objects.all().delete() super(DeleteEditImageTestCase, self).tearDown() def test_delete_image(self): """Deleting an uploaded image works.""" # Upload the image first im = image() r = post(self.client, 'gallery.delete_media', args=['image', im.id]) eq_(200, r.status_code) eq_(0, Image.objects.count()) def test_delete_image_without_permissions(self): """Can't delete an image I didn't create.""" self.client.login(username='******', password='******') img = image() r = post(self.client, 'gallery.delete_media', args=['image', img.id]) eq_(403, r.status_code) eq_(1, Image.objects.count()) def test_delete_own_image(self): """Can delete an image I created.""" self.client.login(username='******', password='******') u = User.objects.get(username='******') img = image(creator=u) r = post(self.client, 'gallery.delete_media', args=['image', img.id]) eq_(200, r.status_code) eq_(0, Image.objects.count()) def test_edit_own_image(self): """Can edit an image I created.""" u = User.objects.get(username='******') img = image(creator=u) r = post(self.client, 'gallery.edit_media', {'description': 'arrr'}, args=['image', img.id]) eq_(200, r.status_code) eq_('arrr', Image.uncached.get().description) def test_edit_image_without_permissions(self): """Can't edit an image I didn't create.""" u = User.objects.get(username='******') img = image(creator=u) r = post(self.client, 'gallery.edit_media', {'description': 'arrr'}, args=['image', img.id]) eq_(403, r.status_code) def test_edit_image_with_permissions(self): """Editing image sets the updated_by field.""" self.client.login(username='******', password='******') img = image() r = post(self.client, 'gallery.edit_media', {'description': 'arrr'}, args=['image', img.id]) eq_(200, r.status_code) eq_('admin', Image.objects.get().updated_by.username)
class UploadVideoTestCase(TestCase): fixtures = ['users.json'] def setUp(self): super(UploadVideoTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username='******', password='******') def tearDown(self): Video.objects.all().delete() super(UploadVideoTestCase, self).tearDown() def _upload_extension(self, ext): with open(TEST_VID[ext]) as f: r = post(self.client, 'gallery.upload_async', {ext: f}, args=['video']) return r def test_upload_video(self): """Uploading a video works.""" r = self._upload_extension('ogv') vid = Video.objects.all()[0] eq_(1, Video.objects.count()) eq_(200, r.status_code) json_r = json.loads(r.content) eq_('success', json_r['status']) file = json_r['file'] eq_('test.ogv', file['name']) eq_(32, file['width']) eq_(32, file['height']) assert file['url'].endswith(vid.get_absolute_url()) eq_('pcraciunoiu', vid.creator.username) eq_(get_draft_title(vid.creator), vid.title) eq_('Autosaved draft.', vid.description) eq_('en-US', vid.locale) with open(TEST_VID['ogv']) as f: eq_(f.read(), vid.ogv.read()) def test_delete_video_ogv(self): """Deleting an uploaded video works.""" # Upload the video first self._upload_extension('ogv') vid = Video.objects.all()[0] r = post(self.client, 'gallery.delete_media', args=['video', vid.id]) eq_(200, r.status_code) eq_(0, Video.objects.count()) def test_upload_video_ogv_flv(self): """Upload the same video, in ogv and flv formats""" ogv = open(TEST_VID['ogv']) flv = open(TEST_VID['flv']) post(self.client, 'gallery.upload_async', {'ogv': ogv, 'flv': flv}, args=['video']) ogv.close() flv.close() vid = Video.objects.all()[0] assert vid.ogv.url.endswith('098f6b.ogv') assert vid.flv.url.endswith('098f6b.flv') def test_upload_video_all(self): """Upload the same video, in all formats""" webm = open(TEST_VID['webm']) ogv = open(TEST_VID['ogv']) flv = open(TEST_VID['flv']) post(self.client, 'gallery.upload_async', {'webm': webm, 'ogv': ogv, 'flv': flv}, args=['video']) webm.close() ogv.close() flv.close() vid = Video.objects.all()[0] assert vid.webm.url.endswith('098f6b.webm') assert vid.ogv.url.endswith('098f6b.ogv') assert vid.flv.url.endswith('098f6b.flv') def test_video_required(self): """At least one video format is required to upload.""" r = post(self.client, 'gallery.upload_async', args=['video']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your video.', json_r['message']) eq_('The video has no files associated with it. You must upload one ' 'of the following extensions: webm, ogv, flv.', json_r['errors']['__all__'][0]) def test_upload_video_long_filename(self): """Uploading a video with a filename that's too long fails.""" for k in ('flv', 'ogv', 'webm'): with open('apps/upload/tests/media/a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_worth_' 'more_than_250_characters__a_really_long_filename_yes_' '.jpg')\ as f: r = post(self.client, 'gallery.upload_async', {k: f}, args=['video']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your video.', json_r['message']) message = getattr(forms, 'MSG_' + k.upper() + '_LONG') eq_(message % {'length': 251, 'max': settings.MAX_FILENAME_LENGTH}, json_r['errors'][k][0]) def test_invalid_video_extension(self): """Make sure invalid video extensions are not accepted.""" with open(INVALID_VID) as f: r = post(self.client, 'gallery.upload_async', {'webm': f}, args=['video']) eq_(400, r.status_code) json_r = json.loads(r.content) eq_('error', json_r['status']) eq_('Could not upload your video.', json_r['message']) eq_(forms.MSG_VID_REQUIRED, json_r['errors']['__all__'][0]) def test_upload_thumbnail(self): """Uploading a thumbnail sets the field and adds a poster.""" r = self._upload_extension('thumbnail') vid = Video.objects.all()[0] eq_(1, Video.objects.count()) eq_(200, r.status_code) eq_(150, vid.thumbnail.width) eq_(150, vid.poster.width) def test_upload_draft_video(self): """Uploading draft video works, sets locale too.""" # Upload a thumbnail/poster self._upload_extension('webm') self._upload_extension('thumbnail') vid = Video.uncached.all()[0] assert vid.thumbnail assert vid.poster r = post(self.client, 'gallery.upload', {'locale': 'de', 'title': 'Hasta la vista', 'description': 'Auf wiedersehen!'}, args=['video']) vid = Video.uncached.all()[0] eq_(200, r.status_code) eq_('de', vid.locale) eq_('Hasta la vista', vid.title) eq_('Auf wiedersehen!', vid.description) # Thumbnail and poster generated after form is saved. eq_(150, vid.poster.width) eq_(90, vid.thumbnail.width)
class BrowserIDTestCase(TestCase): fixtures = ['test_users.json'] def setUp(self): # Ensure @ssl_required goes unenforced. settings.DEBUG = True # Set up some easily-testable redirects. settings.LOGIN_REDIRECT_URL = 'SUCCESS' settings.LOGIN_REDIRECT_URL_FAILURE = 'FAILURE' # BrowserID will squawk if this isn't set settings.SITE_URL = 'http://testserver' self.client = LocalizingClient() def test_invalid_post(self): resp = self.client.post(reverse('users.browserid_verify', locale='en-US')) eq_(302, resp.status_code) ok_('FAILURE' in resp['Location']) @mock.patch('users.views._verify_browserid') def test_invalid_assertion(self, _verify_browserid): _verify_browserid.return_value = None resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'bad data'}) eq_(302, resp.status_code) ok_('FAILURE' in resp['Location']) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_django_user(self, _verify_browserid): _verify_browserid.return_value = {'email': '*****@*****.**'} # Posting the fake assertion to browserid_verify should work, with the # actual verification method mocked out. resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('SUCCESS' in resp['Location']) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_explain_popup(self, _verify_browserid): _verify_browserid.return_value = {'email': '*****@*****.**'} resp = self.client.get(reverse('home', locale='en-US')) # Posting the fake assertion to browserid_verify should work, with the # actual verification method mocked out. resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_('1', resp.cookies.get('browserid_explained').value) resp = self.client.get(reverse('users.logout'), locale='en-US') # even after logout, cookie should prevent the toggle resp = self.client.get(reverse('home', locale='en-US')) eq_('1', self.client.cookies.get('browserid_explained').value) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_mindtouch_user(self, _verify_browserid): if not settings.DEKIWIKI_ENDPOINT: # Don't even bother with this test, if there's no MindTouch API raise SkipTest() mt_email = '*****@*****.**' _verify_browserid.return_value = {'email': mt_email} # Probably overkill but let's be sure we're testing the right thing. try: User.objects.get(email=mt_email) ok_(False, "The MindTouch user shouldn't exist in Django yet.") except User.DoesNotExist: pass if not getattr(settings, 'DEKIWIKI_MOCK', False): # HACK: Ensure that expected user details are in MindTouch when not # mocking the API user_xml = MINDTOUCH_USER_XML % dict(username="******", email=mt_email, fullname="None", status="active", language="", timezone="-08:00", role="Contributor") DekiUserBackend.put_mindtouch_user(deki_user_id='=testaccount', user_xml=user_xml) deki_user = DekiUserBackend.get_deki_user_by_email(mt_email) ok_(deki_user is not None, "The MindTouch user should exist") # Posting the fake assertion to browserid_verify should work, with the # actual verification method mocked out. resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('SUCCESS' in resp['Location']) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) # And, after all the above, there should be a Django user now. try: User.objects.get(email=mt_email) except User.DoesNotExist: ok_(False, "The MindTouch user should exist in Django now.") @attr('current') @mock.patch('dekicompat.backends.DekiUserBackend.get_deki_user_by_email') @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_mt_disabled(self, _verify_browserid, mock_get_deki_user_by_email): """On valid browserid assertion, when Django user is not found, yet MindTouch API disabled, there should be no attempt to look the user up in MindTouch""" mt_email = '*****@*****.**' _verify_browserid.return_value = {'email': mt_email} # HACK: mock has an assert_called_with, but I want something like # never_called or call_count. Instead, I have this: trap = {'was_called': False} def my_get_deki_user_by_email(email): trap['was_called'] = True return False mock_get_deki_user_by_email.side_effect = my_get_deki_user_by_email _old = settings.DEKIWIKI_ENDPOINT settings.DEKIWIKI_ENDPOINT = False resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) settings.DEKIWIKI_ENDPOINT = _old # This should end up being a redirect to register eq_(302, resp.status_code) ok_('browserid_register' in resp['Location']) ok_(not trap['was_called']) @mock_missing_get_deki_user_by_email @mock_missing_get_deki_user @mock_post_mindtouch_user @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_new_account_creation(self, _verify_browserid): new_username = '******' new_email = '*****@*****.**' _verify_browserid.return_value = {'email': new_email} try: user = User.objects.get(email=new_email) ok_(False, "User for email should not yet exist") except User.DoesNotExist: pass if not getattr(settings, 'DEKIWIKI_MOCK', False): # HACK: When not mocking the MindTouch API, ensure that there's no # leftover user with the same name & email as what we want to # register import random rand_num = random.randint(0, 1000000) user_xml = MINDTOUCH_USER_XML % dict( username="******" % (new_username, rand_num), email="%s_%s" % (rand_num, new_email), fullname="", status="inactive", language="", timezone="-08:00", role="Contributor") DekiUserBackend.put_mindtouch_user( deki_user_id='=%s' % new_username, user_xml=user_xml) # Sign in with a verified email, but with no existing account resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) # This should be a redirect to the BrowserID registration page. redir_url = resp['Location'] reg_url = reverse('users.browserid_register', locale='en-US') ok_(reg_url in redir_url) # And, as part of the redirect, the verified email address should be in # our session now. ok_(SESSION_VERIFIED_EMAIL in self.client.session.keys()) verified_email = self.client.session[SESSION_VERIFIED_EMAIL] eq_(new_email, verified_email) # Grab the redirect, assert that there's a create_user form present resp = self.client.get(redir_url) page = pq(resp.content) form = page.find('form#create_user') eq_(1, form.length) # There should be no error lists on first load eq_(0, page.find('.errorlist').length) # Submit the create_user form, with a chosen username resp = self.client.post(redir_url, {'username': '******', 'action': 'register'}) # The submission should result in a redirect to the session's redirect # value eq_(302, resp.status_code) redir_url = resp['Location'] ok_('SUCCESS' in redir_url) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) if settings.DEKIWIKI_ENDPOINT: ok_(self.client.cookies.get('authtoken'), 'Should have set ' 'authtoken cookie for ' 'MindTouch') # Ensure that the user was created, and with the submitted username and # verified email address try: user = User.objects.get(email=new_email) eq_(new_username, user.username) eq_(new_email, user.email) except User.DoesNotExist: ok_(False, "New user should have been created") @mock_missing_get_deki_user_by_email @mock_post_mindtouch_user @mock_put_mindtouch_user @mock_mindtouch_login @mock_get_deki_user @mock.patch('users.views._verify_browserid') def test_valid_assertion_with_existing_account_login(self, _verify_browserid): """ Removed the existing user form: we don't auth the password with MindTouch anymore """ new_email = '*****@*****.**' _verify_browserid.return_value = {'email': new_email} try: User.objects.get(email=new_email) ok_(False, "User for email should not yet exist") except User.DoesNotExist: pass # Sign in with a verified email, but with no existing account resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) # This should be a redirect to the BrowserID registration page. redir_url = resp['Location'] reg_url = reverse('users.browserid_register', locale='en-US') ok_(reg_url in redir_url) # And, as part of the redirect, the verified email address should be in # our session now. ok_(SESSION_VERIFIED_EMAIL in self.client.session.keys()) verified_email = self.client.session[SESSION_VERIFIED_EMAIL] eq_(new_email, verified_email) # Grab the redirect, assert that there's a create_user form present resp = self.client.get(redir_url) page = pq(resp.content) form = page.find('form#existing_user') eq_(0, form.length) @mock.patch('dekicompat.backends.DekiUserBackend.mindtouch_login') @mock.patch('users.views._verify_browserid') def test_mindtouch_disabled_redirect_login(self, _verify_browserid, mock_mindtouch_login): """When DEKIWIKI_ENDPOINT unavailable, skip MindTouch auth.""" _verify_browserid.return_value = {'email': '*****@*****.**'} # HACK: mock has an assert_called_with, but I want something like # never_called or call_count. Instead, I have this: trap = {'was_called': False} def my_mindtouch_login(username, password, force=False): trap['was_called'] = True return False mock_mindtouch_login.side_effect = my_mindtouch_login _old = settings.DEKIWIKI_ENDPOINT settings.DEKIWIKI_ENDPOINT = False resp = self.client.post(reverse('users.browserid_verify', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) settings.DEKIWIKI_ENDPOINT = _old eq_(302, resp.status_code) ok_('SUCCESS' in resp['Location']) # The session should look logged in, now. ok_('_auth_user_id' in self.client.session.keys()) eq_('django_browserid.auth.BrowserIDBackend', self.client.session.get('_auth_user_backend', '')) ok_(not trap['was_called']) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_changing_email(self, _verify_browserid): # just need to be authenticated, not necessarily BrowserID self.client.login(username='******', password='******') _verify_browserid.return_value = {'email': '*****@*****.**'} resp = self.client.post(reverse('users.browserid_change_email', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('profiles/testuser/edit' in resp['Location']) resp = self.client.get(reverse('devmo_profile_edit', locale='en-US', args=['testuser', ])) eq_(200, resp.status_code) doc = pq(resp.content) ok_('*****@*****.**' in doc.find('li#field_email').text()) @mock_get_deki_user_by_email @mock_put_mindtouch_user @mock_mindtouch_login @mock.patch('users.views._verify_browserid') def test_valid_assertion_doesnt_steal_email(self, _verify_browserid): # just need to be authenticated, not necessarily BrowserID self.client.login(username='******', password='******') _verify_browserid.return_value = {'email': '*****@*****.**'} # doesn't change email if the new email already belongs to another user resp = self.client.post(reverse('users.browserid_change_email', locale='en-US'), {'assertion': 'PRETENDTHISISVALID'}) eq_(302, resp.status_code) ok_('change_email' in resp['Location']) resp = self.client.get(reverse('devmo_profile_edit', locale='en-US', args=['testuser', ])) eq_(200, resp.status_code) doc = pq(resp.content) ok_('*****@*****.**' in doc.find('li#field_email').text())
class ProfileViewsTest(test_utils.TestCase): def setUp(self): self.client = LocalizingClient() def test_profile_view(self): """A user profile can be viewed""" (user, deki_user, profile) = self._create_profile() url = reverse('devmo.views.profile_view', args=(user.username,)) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(profile.user.username, doc.find('#profile-head.vcard .nickname').text()) eq_(profile.fullname, doc.find('#profile-head.vcard .fn').text()) eq_(profile.title, doc.find('#profile-head.vcard .title').text()) eq_(profile.organization, doc.find('#profile-head.vcard .org').text()) eq_(profile.location, doc.find('#profile-head.vcard .loc').text()) eq_(profile.irc_nickname, doc.find('#profile-head.vcard .irc').text()) eq_(profile.bio, doc.find('#profile-head.vcard .bio').text()) @attr('current') def test_profile_edit(self): (user, deki_user, profile) = self._create_profile() url = reverse('devmo.views.profile_view', args=(user.username,)) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(0, doc.find('#profile-head .edit .button').length) self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_view', args=(user.username,)) r = self.client.get(url, follow=True) doc = pq(r.content) edit_button = doc.find('#profile-head .edit .button') eq_(1, edit_button.length) url = edit_button.attr('href') r = self.client.get(url, follow=True) doc = pq(r.content) eq_(profile.fullname, doc.find('#profile-edit input[name="fullname"]').val()) eq_(profile.title, doc.find('#profile-edit input[name="title"]').val()) eq_(profile.organization, doc.find('#profile-edit input[name="organization"]').val()) eq_(profile.location, doc.find('#profile-edit input[name="location"]').val()) eq_(profile.irc_nickname, doc.find('#profile-edit input[name="irc_nickname"]').val()) new_attrs = dict( email="*****@*****.**", fullname="Another Name", title="Another title", organization="Another org", ) r = self.client.post(url, new_attrs, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) eq_(new_attrs['fullname'], doc.find('#profile-head .main .fn').text()) eq_(new_attrs['title'], doc.find('#profile-head .info .title').text()) eq_(new_attrs['organization'], doc.find('#profile-head .info .org').text()) profile = UserProfile.objects.get(user__username=user.username) eq_(new_attrs['fullname'], profile.fullname) eq_(new_attrs['title'], profile.title) eq_(new_attrs['organization'], profile.organization) def test_profile_edit_websites(self): (user, deki_user, profile) = self._create_profile() self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username,)) r = self.client.get(url, follow=True) doc = pq(r.content) test_sites = { u'website': u'http://example.com/', u'twitter': u'http://twitter.com/lmorchard', u'github': u'http://github.com/lmorchard', u'stackoverflow': u'http://stackoverflow.com/users/lmorchard', } # Scrape out the existing significant form field values. form = dict() for fn in ('email', 'fullname', 'title', 'organization', 'location', 'irc_nickname', 'bio', 'interests'): form[fn] = doc.find('#profile-edit *[name="%s"]' % fn).val() # Fill out the form with websites. form.update(dict(('websites_%s' % k, v) for k, v in test_sites.items())) # Submit the form, verify redirect to profile detail r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) # Verify the websites are saved in the profile. eq_(test_sites, p.websites) # Verify the saved websites appear in the editing form url = reverse('devmo.views.profile_edit', args=(user.username,)) r = self.client.get(url, follow=True) doc = pq(r.content) for k, v in test_sites.items(): eq_(v, doc.find('#profile-edit *[name="websites_%s"]' % k).val()) # Come up with some bad sites, either invalid URL or bad URL prefix bad_sites = { u'website': u'HAHAHA WHAT IS A WEBSITE', u'twitter': u'http://facebook.com/lmorchard', u'stackoverflow': u'http://overqueueblah.com/users/lmorchard', } form.update(dict(('websites_%s' % k, v) for k, v in bad_sites.items())) # Submit the form, verify errors for all of the bad sites r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-edit').length) tmpl = '#profile-edit #elsewhere .%s .errorlist' for n in ('website', 'twitter', 'stackoverflow'): eq_(1, doc.find(tmpl % n).length) def test_profile_edit_interests(self): (user, deki_user, profile) = self._create_profile() self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username,)) r = self.client.get(url, follow=True) doc = pq(r.content) form = dict() for fn in ('email', 'fullname', 'title', 'organization', 'location', 'irc_nickname', 'bio', 'interests'): form[fn] = doc.find('#profile-edit *[name="%s"]' % fn).val() test_tags = ['javascript', 'css', 'canvas', 'html', 'homebrewing'] form['interests'] = ', '.join(test_tags) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) result_tags = [t.name.replace('profile:interest:', '') for t in p.tags.all_ns('profile:interest:')] result_tags.sort() test_tags.sort() eq_(test_tags, result_tags) test_expertise = ['css', 'canvas'] form['expertise'] = ', '.join(test_expertise) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) result_tags = [t.name.replace('profile:expertise:', '') for t in p.tags.all_ns('profile:expertise')] result_tags.sort() test_expertise.sort() eq_(test_expertise, result_tags) # Now, try some expertise tags not covered in interests test_expertise = ['css', 'canvas', 'mobile', 'movies'] form['expertise'] = ', '.join(test_expertise) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('.error #id_expertise').length) def _create_profile(self): """Create a user, deki_user, and a profile for a test account""" user = User.objects.create_user('tester23', '*****@*****.**', 'trustno1') deki_user = DekiUser(id=0, username='******', fullname='Tester Twentythree', email='*****@*****.**', gravatar='', profile_url=None) profile = UserProfile() profile.user = user profile.fullname = "Tester Twentythree" profile.title = "Spaceship Pilot" profile.organization = "UFO" profile.location = "Outer Space" profile.irc_nickname = "ircuser" profile.bio = "I am a freaky space alien." profile.save() return (user, deki_user, profile) def _break(self, url, r): logging.debug("URL %s" % url) logging.debug("STAT %s" % r.status_code) logging.debug("HEAD %s" % r.items()) logging.debug("CONT %s" % r.content) ok_(False)
class MobileAAQTests(MobileTestCase): fixtures = ['users.json', 'questions.json'] data = { 'title': 'A test question', 'content': 'I have this question that I hope...', 'sites_affected': 'http://example.com', 'ff_version': '3.6.6', 'os': 'Intel Mac OS X 10.6', 'plugins': '* Shockwave Flash 10.1 r53', 'useragent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X ' '10.6; en-US; rv:1.9.2.6) Gecko/20100625 ' 'Firefox/3.6.6' } def setUp(self): self.client = LocalizingClient() super(MobileAAQTests, self).setUp() def _new_question(self, post_it=False): """Post a new question and return the response.""" url = urlparams(reverse('questions.new_question'), product='desktop', category='d1', search='A test question', showform=1) if post_it: return self.client.post(url, self.data, follow=True) return self.client.get(url, follow=True) def test_logged_out(self): """New question is posted through mobile.""" response = self._new_question() eq_(200, response.status_code) self.assertTemplateUsed(response, 'questions/mobile/new_question_login.html') @mock.patch.object(Site.objects, 'get_current') def test_logged_in_get(self, get_current): """New question is posted through mobile.""" get_current.return_value.domain = 'testserver' self.client.login(username='******', password='******') response = self._new_question() eq_(200, response.status_code) self.assertTemplateUsed(response, 'questions/mobile/new_question.html') @mock.patch.object(Site.objects, 'get_current') def test_logged_in_post(self, get_current): """New question is posted through mobile.""" get_current.return_value.domain = 'testserver' self.client.login(username='******', password='******') response = self._new_question(post_it=True) eq_(200, response.status_code) assert Question.objects.filter(title='A test question') @mock.patch.object(Site.objects, 'get_current') def test_aaq_new_question_inactive(self, get_current): """New question is posted through mobile.""" get_current.return_value.domain = 'testserver' # Log in first. self.client.login(username='******', password='******') # Then become inactive. u = User.objects.get(username='******') u.is_active = False u.save() response = self._new_question(post_it=True) eq_(200, response.status_code) self.assertTemplateUsed(response, 'questions/mobile/confirm_email.html')
class ChangeEmailTestCase(TestCase): fixtures = ['test_users.json'] def setUp(self): self.client = LocalizingClient() @mock.patch_object(Site.objects, 'get_current') def test_user_change_email(self, get_current): """Send email to change user's email and then change it.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') # Attempt to change email. response = self.client.post(reverse('users.change_email'), {'email': '*****@*****.**'}, follow=True) eq_(200, response.status_code) # Be notified to click a confirmation link. eq_(1, len(mail.outbox)) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] assert ec.activation_key in mail.outbox[0].body eq_('*****@*****.**', ec.email) # Visit confirmation link to change email. response = self.client.get(reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) u = User.objects.get(username='******') eq_('*****@*****.**', u.email) @mock_get_deki_user @mock_put_mindtouch_user @mock.patch_object(Site.objects, 'get_current') def test_user_change_email_updates_mindtouch(self, get_current): """Send email to change user's email and then change it.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') # Attempt to change email. response = self.client.post(reverse('users.change_email'), {'email': '*****@*****.**'}, follow=True) eq_(200, response.status_code) # Be notified to click a confirmation link. eq_(1, len(mail.outbox)) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] assert ec.activation_key in mail.outbox[0].body eq_('*****@*****.**', ec.email) # Visit confirmation link to change email. response = self.client.get(reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) u = User.objects.get(username='******') eq_('*****@*****.**', u.email) if not settings.DEKIWIKI_MOCK: deki_id = u.get_profile().deki_user_id doc = get_deki_user_doc(u) eq_(str(deki_id), doc('user').attr('id')) eq_('*****@*****.**', doc('user').find('email').text()) def test_user_change_email_same(self): """Changing to same email shows validation error.""" self.client.login(username='******', password='******') user = User.objects.get(username='******') user.email = '*****@*****.**' user.save() response = self.client.post(reverse('users.change_email'), {'email': user.email}) eq_(200, response.status_code) doc = pq(response.content) eq_('This is your current email.', doc('ul.errorlist').text()) def test_user_change_email_duplicate(self): """Changing to same email shows validation error.""" self.client.login(username='******', password='******') email = '*****@*****.**' response = self.client.post(reverse('users.change_email'), {'email': email}) eq_(200, response.status_code) doc = pq(response.content) eq_('A user with that email address already exists.', doc('ul.errorlist').text()) @mock.patch_object(Site.objects, 'get_current') def test_user_confirm_email_duplicate(self, get_current): """If we detect a duplicate email when confirming an email change, don't change it and notify the user.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') old_email = User.objects.get(username='******').email new_email = '*****@*****.**' response = self.client.post(reverse('users.change_email'), {'email': new_email}) eq_(200, response.status_code) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] # Before new email is confirmed, give the same email to a user other_user = User.objects.filter(username='******')[0] other_user.email = new_email other_user.save() # Visit confirmation link and verify email wasn't changed. response = self.client.get(reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) doc = pq(response.content) eq_('Unable to change email for user testuser', doc('.main h1').text()) u = User.objects.get(username='******') eq_(old_email, u.email)
class ProfileViewsTest(test_utils.TestCase): def setUp(self): self.client = LocalizingClient() def test_profile_view(self): """A user profile can be viewed""" (user, deki_user, profile) = self._create_profile() url = reverse('devmo.views.profile_view', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(profile.user.username, doc.find('#profile-head.vcard .nickname').text()) eq_(profile.fullname, doc.find('#profile-head.vcard .fn').text()) eq_(profile.title, doc.find('#profile-head.vcard .title').text()) eq_(profile.organization, doc.find('#profile-head.vcard .org').text()) eq_(profile.location, doc.find('#profile-head.vcard .loc').text()) eq_(profile.irc_nickname, doc.find('#profile-head.vcard .irc').text()) eq_(profile.bio, doc.find('#profile-head.vcard .bio').text()) @attr('current') def test_profile_edit(self): (user, deki_user, profile) = self._create_profile() url = reverse('devmo.views.profile_view', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(0, doc.find('#profile-head .edit .button').length) self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_view', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) edit_button = doc.find('#profile-head .edit .button') eq_(1, edit_button.length) url = edit_button.attr('href') r = self.client.get(url, follow=True) doc = pq(r.content) eq_(profile.fullname, doc.find('#profile-edit input[name="fullname"]').val()) eq_(profile.title, doc.find('#profile-edit input[name="title"]').val()) eq_(profile.organization, doc.find('#profile-edit input[name="organization"]').val()) eq_(profile.location, doc.find('#profile-edit input[name="location"]').val()) eq_(profile.irc_nickname, doc.find('#profile-edit input[name="irc_nickname"]').val()) new_attrs = dict( email="*****@*****.**", fullname="Another Name", title="Another title", organization="Another org", ) r = self.client.post(url, new_attrs, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) eq_(new_attrs['fullname'], doc.find('#profile-head .main .fn').text()) eq_(new_attrs['title'], doc.find('#profile-head .info .title').text()) eq_(new_attrs['organization'], doc.find('#profile-head .info .org').text()) profile = UserProfile.objects.get(user__username=user.username) eq_(new_attrs['fullname'], profile.fullname) eq_(new_attrs['title'], profile.title) eq_(new_attrs['organization'], profile.organization) def test_profile_edit_websites(self): (user, deki_user, profile) = self._create_profile() self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) test_sites = { u'website': u'http://example.com/', u'twitter': u'http://twitter.com/lmorchard', u'github': u'http://github.com/lmorchard', u'stackoverflow': u'http://stackoverflow.com/users/lmorchard', } # Scrape out the existing significant form field values. form = dict() for fn in ('email', 'fullname', 'title', 'organization', 'location', 'irc_nickname', 'bio', 'interests'): form[fn] = doc.find('#profile-edit *[name="%s"]' % fn).val() # Fill out the form with websites. form.update(dict( ('websites_%s' % k, v) for k, v in test_sites.items())) # Submit the form, verify redirect to profile detail r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) # Verify the websites are saved in the profile. eq_(test_sites, p.websites) # Verify the saved websites appear in the editing form url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) for k, v in test_sites.items(): eq_(v, doc.find('#profile-edit *[name="websites_%s"]' % k).val()) # Come up with some bad sites, either invalid URL or bad URL prefix bad_sites = { u'website': u'HAHAHA WHAT IS A WEBSITE', u'twitter': u'http://facebook.com/lmorchard', u'stackoverflow': u'http://overqueueblah.com/users/lmorchard', } form.update(dict(('websites_%s' % k, v) for k, v in bad_sites.items())) # Submit the form, verify errors for all of the bad sites r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-edit').length) tmpl = '#profile-edit #elsewhere .%s .errorlist' for n in ('website', 'twitter', 'stackoverflow'): eq_(1, doc.find(tmpl % n).length) def test_profile_edit_interests(self): (user, deki_user, profile) = self._create_profile() self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) form = dict() for fn in ('email', 'fullname', 'title', 'organization', 'location', 'irc_nickname', 'bio', 'interests'): form[fn] = doc.find('#profile-edit *[name="%s"]' % fn).val() test_tags = ['javascript', 'css', 'canvas', 'html', 'homebrewing'] form['interests'] = ', '.join(test_tags) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) result_tags = [ t.name.replace('profile:interest:', '') for t in p.tags.all_ns('profile:interest:') ] result_tags.sort() test_tags.sort() eq_(test_tags, result_tags) test_expertise = ['css', 'canvas'] form['expertise'] = ', '.join(test_expertise) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) result_tags = [ t.name.replace('profile:expertise:', '') for t in p.tags.all_ns('profile:expertise') ] result_tags.sort() test_expertise.sort() eq_(test_expertise, result_tags) # Now, try some expertise tags not covered in interests test_expertise = ['css', 'canvas', 'mobile', 'movies'] form['expertise'] = ', '.join(test_expertise) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('.error #id_expertise').length) def _create_profile(self): """Create a user, deki_user, and a profile for a test account""" user = User.objects.create_user('tester23', '*****@*****.**', 'trustno1') deki_user = DekiUser(id=0, username='******', fullname='Tester Twentythree', email='*****@*****.**', gravatar='', profile_url=None) profile = UserProfile() profile.user = user profile.fullname = "Tester Twentythree" profile.title = "Spaceship Pilot" profile.organization = "UFO" profile.location = "Outer Space" profile.irc_nickname = "ircuser" profile.bio = "I am a freaky space alien." profile.save() return (user, deki_user, profile) def _break(self, url, r): logging.debug("URL %s" % url) logging.debug("STAT %s" % r.status_code) logging.debug("HEAD %s" % r.items()) logging.debug("CONT %s" % r.content) ok_(False)
class GalleryUploadTestCase(TestCase): fixtures = ['users.json'] def setUp(self): super(GalleryUploadTestCase, self).setUp() self.client = LocalizingClient() self.client.login(username='******', password='******') self.u = User.objects.get(username='******') def tearDown(self): Image.objects.all().delete() Video.objects.all().delete() super(GalleryUploadTestCase, self).tearDown() def test_image_draft_shows(self): """The image draft is loaded for this user.""" image(is_draft=True, creator=self.u) response = get(self.client, 'gallery.gallery', args=['image']) eq_(200, response.status_code) doc = pq(response.content) assert doc('.file.preview img').attr('src').endswith('098f6b.jpg') eq_(1, doc('.file.preview img').length) def test_video_draft_shows(self): """The video draft is loaded for this user.""" video(is_draft=True, creator=self.u) response = get(self.client, 'gallery.gallery', args=['image']) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_(3, doc('#gallery-upload-video .preview input').length) def test_image_draft_post(self): """Posting to the page saves the field values for the image draft.""" image(is_draft=True, creator=self.u) response = post(self.client, 'gallery.gallery', { 'description': '??', 'title': 'test' }, args=['image']) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_('??', doc('#gallery-upload-modal textarea').html()) eq_('test', doc('#gallery-upload-modal input[name="title"]').val()) def test_video_draft_post(self): """Posting to the page saves the field values for the video draft.""" video(is_draft=True, creator=self.u) response = post(self.client, 'gallery.gallery', {'title': 'zTestz'}, args=['image']) eq_(200, response.status_code) doc = pq(response.content) # Preview for all 3 video formats: flv, ogv, webm eq_('zTestz', doc('#gallery-upload-modal input[name="title"]').val()) def test_modal_locale_selected(self): """Locale value is selected for upload modal.""" response = get(self.client, 'gallery.gallery', args=['image'], locale='fr') doc = pq(response.content) eq_('fr', doc('#gallery-upload-image option[selected="selected"]').val()) eq_('fr', doc('#gallery-upload-video option[selected="selected"]').val()) def test_invalid_messages(self): # TODO(paul) POSTing invalid data shows error messages and pre-fills raise SkipTest
class ProfileViewsTest(TestCase): fixtures = ['test_users.json'] def setUp(self): self.old_debug = settings.DEBUG settings.DEBUG = True self.client = LocalizingClient() self.client.logout() def tearDown(self): settings.DEBUG = self.old_debug def _get_current_form_field_values(self, doc): # Scrape out the existing significant form field values. form = dict() for fn in ('email', 'fullname', 'title', 'organization', 'location', 'irc_nickname', 'bio', 'interests'): form[fn] = doc.find('#profile-edit *[name="%s"]' % fn).val() return form @attr('docs_activity') def test_profile_view(self): """A user profile can be viewed""" profile = UserProfile.objects.get(user__username='******') user = profile.user url = reverse('devmo.views.profile_view', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(profile.user.username, doc.find('#profile-head.vcard .nickname').text()) eq_(profile.fullname, doc.find('#profile-head.vcard .fn').text()) eq_(profile.title, doc.find('#profile-head.vcard .title').text()) eq_(profile.organization, doc.find('#profile-head.vcard .org').text()) eq_(profile.location, doc.find('#profile-head.vcard .loc').text()) eq_('IRC: ' + profile.irc_nickname, doc.find('#profile-head.vcard .irc').text()) eq_(profile.bio, doc.find('#profile-head.vcard .bio').text()) def test_my_profile_view(self): u = User.objects.get(username='******') self.client.login(username=u.username, password=TESTUSER_PASSWORD) resp = self.client.get('/profile/') eq_(302, resp.status_code) ok_( reverse('devmo.views.profile_view', args=( u.username, )) in resp['Location']) def test_bug_698971(self): """A non-numeric page number should not cause an error""" (user, profile) = create_profile() url = '%s?page=asdf' % reverse('devmo.views.profile_view', args=(user.username, )) try: self.client.get(url, follow=True) except PageNotAnInteger: ok_(False, "Non-numeric page number should not cause an error") def test_profile_edit(self): profile = UserProfile.objects.get(user__username='******') user = profile.user url = reverse('devmo.views.profile_view', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(0, doc.find('#profile-head .edit .button').length) self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_view', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) edit_button = doc.find('#profile-head .edit #edit-profile') eq_(1, edit_button.length) url = edit_button.attr('href') r = self.client.get(url, follow=True) doc = pq(r.content) eq_(profile.fullname, doc.find('#profile-edit input[name="fullname"]').val()) eq_(profile.title, doc.find('#profile-edit input[name="title"]').val()) eq_(profile.organization, doc.find('#profile-edit input[name="organization"]').val()) eq_(profile.location, doc.find('#profile-edit input[name="location"]').val()) eq_(profile.irc_nickname, doc.find('#profile-edit input[name="irc_nickname"]').val()) new_attrs = dict( email='*****@*****.**', fullname="Another Name", title="Another title", organization="Another org", ) r = self.client.post(url, new_attrs, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) eq_(new_attrs['fullname'], doc.find('#profile-head .main .fn').text()) eq_(new_attrs['title'], doc.find('#profile-head .info .title').text()) eq_(new_attrs['organization'], doc.find('#profile-head .info .org').text()) profile = UserProfile.objects.get(user__username=user.username) eq_(new_attrs['fullname'], profile.fullname) eq_(new_attrs['title'], profile.title) eq_(new_attrs['organization'], profile.organization) def test_my_profile_edit(self): u = User.objects.get(username='******') self.client.login(username=u.username, password=TESTUSER_PASSWORD) resp = self.client.get('/profile/edit') eq_(302, resp.status_code) ok_( reverse('devmo.views.profile_edit', args=( u.username, )) in resp['Location']) def test_profile_edit_beta(self): user = User.objects.get(username='******') self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) eq_(None, doc.find('input#id_beta').attr('checked')) form = self._get_current_form_field_values(doc) form['beta'] = True r = self.client.post(url, form, follow=True) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) eq_('checked', doc.find('input#id_beta').attr('checked')) def test_profile_edit_websites(self): user = User.objects.get(username='******') self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) test_sites = { u'website': u'http://example.com/', u'twitter': u'http://twitter.com/lmorchard', u'github': u'http://github.com/lmorchard', u'stackoverflow': u'http://stackoverflow.com/users/lmorchard', } form = self._get_current_form_field_values(doc) # Fill out the form with websites. form.update(dict( ('websites_%s' % k, v) for k, v in test_sites.items())) # Submit the form, verify redirect to profile detail r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) # Verify the websites are saved in the profile. eq_(test_sites, p.websites) # Verify the saved websites appear in the editing form url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) for k, v in test_sites.items(): eq_(v, doc.find('#profile-edit *[name="websites_%s"]' % k).val()) # Come up with some bad sites, either invalid URL or bad URL prefix bad_sites = { u'website': u'HAHAHA WHAT IS A WEBSITE', u'twitter': u'http://facebook.com/lmorchard', u'stackoverflow': u'http://overqueueblah.com/users/lmorchard', } form.update(dict(('websites_%s' % k, v) for k, v in bad_sites.items())) # Submit the form, verify errors for all of the bad sites r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-edit').length) tmpl = '#profile-edit #elsewhere .%s .errorlist' for n in ('website', 'twitter', 'stackoverflow'): eq_(1, doc.find(tmpl % n).length) def test_profile_edit_interests(self): user = User.objects.get(username='******') self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) test_tags = ['javascript', 'css', 'canvas', 'html', 'homebrewing'] form = self._get_current_form_field_values(doc) form['interests'] = ', '.join(test_tags) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) result_tags = [ t.name.replace('profile:interest:', '') for t in p.tags.all_ns('profile:interest:') ] result_tags.sort() test_tags.sort() eq_(test_tags, result_tags) test_expertise = ['css', 'canvas'] form['expertise'] = ', '.join(test_expertise) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('#profile-head').length) p = UserProfile.objects.get(user=user) result_tags = [ t.name.replace('profile:expertise:', '') for t in p.tags.all_ns('profile:expertise') ] result_tags.sort() test_expertise.sort() eq_(test_expertise, result_tags) # Now, try some expertise tags not covered in interests test_expertise = ['css', 'canvas', 'mobile', 'movies'] form['expertise'] = ', '.join(test_expertise) r = self.client.post(url, form, follow=True) doc = pq(r.content) eq_(1, doc.find('.error #id_expertise').length) def test_bug_709938_interests(self): user = User.objects.get(username='******') self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) doc = pq(r.content) test_tags = [ u'science,Technology,paradox,knowledge,modeling,big data,' u'vector,meme,heuristics,harmony,mathesis universalis,' u'symmetry,mathematics,computer graphics,field,chemistry,' u'religion,astronomy,physics,biology,literature,' u'spirituality,Art,Philosophy,Psychology,Business,Music,' u'Computer Science' ] form = self._get_current_form_field_values(doc) form['interests'] = test_tags r = self.client.post(url, form, follow=True) eq_(200, r.status_code) doc = pq(r.content) eq_(1, doc.find('ul.errorlist li').length) assert ('Ensure this value has at most 255 characters' in doc.find('ul.errorlist li').text()) def test_bug_698126_l10n(self): """Test that the form field names are localized""" user = User.objects.get(username='******') self.client.login(username=user.username, password=TESTUSER_PASSWORD) url = reverse('devmo.views.profile_edit', args=(user.username, )) r = self.client.get(url, follow=True) for field in r.context['form'].fields: # if label is localized it's a lazy proxy object ok_( not isinstance(r.context['form'].fields[field].label, basestring), 'Field %s is a string!' % field) def _break(self, url, r): logging.debug("URL %s" % url) logging.debug("STAT %s" % r.status_code) logging.debug("HEAD %s" % r.items()) logging.debug("CONT %s" % r.content) ok_(False)
def test_midair_section_collision(self): """If both a revision and the edited section has changed, then a section edit is a collision.""" client = LocalizingClient() client.login(username='******', password='******') doc, rev = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) replace_1 = """ <h1 id="s2">replace</h1> <p>replace</p> """ replace_2 = """ <h1 id="s2">first replace</h1> <p>first replace</p> """ data = {'form': 'rev', 'content': rev.content} # Edit #1 starts... resp = client.get('%s?section=s2' % reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id1 = page.find('input[name="current_rev"]').attr('value') # Edit #2 starts... resp = client.get('%s?section=s2' % reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id2 = page.find('input[name="current_rev"]').attr('value') # Edit #2 submits successfully data.update({ 'form': 'rev', 'content': replace_2, 'current_rev': rev_id2 }) resp = client.post( '%s?section=s2&raw=true' % reverse('wiki.edit_document', args=[doc.slug]), data) eq_(302, resp.status_code) # Edit #1 submits, but since it's the same section, there's a collision data.update({ 'form': 'rev', 'content': replace_1, 'current_rev': rev_id1 }) resp = client.post( '%s?section=s2&raw=true' % reverse('wiki.edit_document', args=[doc.slug]), data) # With the raw API, we should get a 409 Conflict on collision. eq_(409, resp.status_code)
def test_midair_section_merge(self): """If a page was changed while someone was editing, but the changes didn't affect the specific section being edited, then ignore the midair warning""" client = LocalizingClient() client.login(username='******', password='******') doc, rev = doc_rev(""" <h1 id="s1">Head 1</h1> <p>test</p> <p>test</p> <h1 id="s2">Head 2</h1> <p>test</p> <p>test</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """) replace_1 = """ <h1 id="s1">replace</h1> <p>replace</p> """ replace_2 = """ <h1 id="s2">replace</h1> <p>replace</p> """ expected = """ <h1 id="s1">replace</h1> <p>replace</p> <h1 id="s2">replace</h1> <p>replace</p> <h1 id="s3">Head 3</h1> <p>test</p> <p>test</p> """ data = {'form': 'rev', 'content': rev.content} # Edit #1 starts... resp = client.get('%s?section=s1' % reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id1 = page.find('input[name="current_rev"]').attr('value') # Edit #2 starts... resp = client.get('%s?section=s2' % reverse('wiki.edit_document', args=[doc.slug])) page = pq(resp.content) rev_id2 = page.find('input[name="current_rev"]').attr('value') # Edit #2 submits successfully data.update({ 'form': 'rev', 'content': replace_2, 'current_rev': rev_id2 }) resp = client.post( '%s?section=s2&raw=true' % reverse('wiki.edit_document', args=[doc.slug]), data) eq_(302, resp.status_code) # Edit #1 submits, but since it's a different section, there's no # mid-air collision data.update({ 'form': 'rev', 'content': replace_1, 'current_rev': rev_id1 }) resp = client.post( '%s?section=s1&raw=true' % reverse('wiki.edit_document', args=[doc.slug]), data) # No conflict, but we should get a 205 Reset as an indication that the # page needs a refresh. eq_(205, resp.status_code) # Finally, make sure that all the edits landed response = client.get('%s?raw=true' % reverse('wiki.document', args=[doc.slug])) eq_(normalize_html(expected), normalize_html(response.content)) # Also, ensure that the revision is slipped into the headers eq_(unicode(Document.uncached.get(slug=doc.slug).current_revision.id), unicode(response['x-kuma-revision']))
class ChangeEmailTestCase(TestCase): fixtures = ['test_users.json'] def setUp(self): self.client = LocalizingClient() @mock.patch_object(Site.objects, 'get_current') def test_user_change_email(self, get_current): """Send email to change user's email and then change it.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') # Attempt to change email. response = self.client.post(reverse('users.change_email'), {'email': '*****@*****.**'}, follow=True) eq_(200, response.status_code) # Be notified to click a confirmation link. eq_(1, len(mail.outbox)) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] assert ec.activation_key in mail.outbox[0].body eq_('*****@*****.**', ec.email) # Visit confirmation link to change email. response = self.client.get( reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) u = User.objects.get(username='******') eq_('*****@*****.**', u.email) @mock_get_deki_user @mock_put_mindtouch_user @mock.patch_object(Site.objects, 'get_current') def test_user_change_email_updates_mindtouch(self, get_current): """Send email to change user's email and then change it.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') # Attempt to change email. response = self.client.post(reverse('users.change_email'), {'email': '*****@*****.**'}, follow=True) eq_(200, response.status_code) # Be notified to click a confirmation link. eq_(1, len(mail.outbox)) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] assert ec.activation_key in mail.outbox[0].body eq_('*****@*****.**', ec.email) # Visit confirmation link to change email. response = self.client.get( reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) u = User.objects.get(username='******') eq_('*****@*****.**', u.email) if not settings.DEKIWIKI_MOCK: deki_id = u.get_profile().deki_user_id doc = get_deki_user_doc(u) eq_(str(deki_id), doc('user').attr('id')) eq_('*****@*****.**', doc('user').find('email').text()) def test_user_change_email_same(self): """Changing to same email shows validation error.""" self.client.login(username='******', password='******') user = User.objects.get(username='******') user.email = '*****@*****.**' user.save() response = self.client.post(reverse('users.change_email'), {'email': user.email}) eq_(200, response.status_code) doc = pq(response.content) eq_('This is your current email.', doc('ul.errorlist').text()) def test_user_change_email_duplicate(self): """Changing to same email shows validation error.""" self.client.login(username='******', password='******') email = '*****@*****.**' response = self.client.post(reverse('users.change_email'), {'email': email}) eq_(200, response.status_code) doc = pq(response.content) eq_('A user with that email address already exists.', doc('ul.errorlist').text()) @mock.patch_object(Site.objects, 'get_current') def test_user_confirm_email_duplicate(self, get_current): """If we detect a duplicate email when confirming an email change, don't change it and notify the user.""" get_current.return_value.domain = 'su.mo.com' self.client.login(username='******', password='******') old_email = User.objects.get(username='******').email new_email = '*****@*****.**' response = self.client.post(reverse('users.change_email'), {'email': new_email}) eq_(200, response.status_code) assert mail.outbox[0].subject.find('Please confirm your') == 0 ec = EmailChange.objects.all()[0] # Before new email is confirmed, give the same email to a user other_user = User.objects.filter(username='******')[0] other_user.email = new_email other_user.save() # Visit confirmation link and verify email wasn't changed. response = self.client.get( reverse('users.confirm_email', args=[ec.activation_key])) eq_(200, response.status_code) doc = pq(response.content) eq_('Unable to change email for user testuser', doc('.main h1').text()) u = User.objects.get(username='******') eq_(old_email, u.email)
def test_review_tags(self, get_current): get_current.return_value.domain = 'su.mo.com' """Review tags can be managed on document revisions""" client = LocalizingClient() client.login(username='******', password='******') # Create a new doc with one review tag data = new_document_data() data.update({'review_tags': ['technical']}) response = client.post(reverse('wiki.new_document'), data) # Ensure there's now a doc with that expected tag in its newest # revision doc = Document.objects.get(slug="a-test-article") rev = doc.revisions.order_by('-id').all()[0] review_tags = [x.name for x in rev.review_tags.all()] eq_(['technical'], review_tags) # Now, post an update with two tags data.update({ 'form': 'rev', 'review_tags': ['editorial', 'technical'], }) response = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) # Ensure the doc's newest revision has both tags. doc = Document.objects.get(slug="a-test-article") rev = doc.revisions.order_by('-id').all()[0] review_tags = [x.name for x in rev.review_tags.all()] review_tags.sort() eq_(['editorial', 'technical'], review_tags) # Now, ensure that warning boxes appear for the review tags. response = client.get(reverse('wiki.document', args=[doc.slug]), data) page = pq(response.content) eq_(1, page.find('.warning.review-technical').length) eq_(1, page.find('.warning.review-editorial').length) # Ensure the page appears on the listing pages response = client.get(reverse('wiki.list_review')) eq_( 1, pq(response.content).find("ul.documents li a:contains('%s')" % doc.title).length) response = client.get( reverse('wiki.list_review_tag', args=('technical', ))) eq_( 1, pq(response.content).find("ul.documents li a:contains('%s')" % doc.title).length) response = client.get( reverse('wiki.list_review_tag', args=('editorial', ))) eq_( 1, pq(response.content).find("ul.documents li a:contains('%s')" % doc.title).length) # Also, ensure that the page appears in the proper feeds # HACK: Too lazy to parse the XML. Lazy lazy. response = client.get( reverse('wiki.feeds.list_review', args=('atom', ))) ok_('<entry><title>%s</title>' % doc.title in response.content) response = client.get( reverse('wiki.feeds.list_review_tag', args=( 'atom', 'technical', ))) ok_('<entry><title>%s</title>' % doc.title in response.content) response = client.get( reverse('wiki.feeds.list_review_tag', args=( 'atom', 'editorial', ))) ok_('<entry><title>%s</title>' % doc.title in response.content) # Post an edit that removes one of the tags. data.update({ 'form': 'rev', 'review_tags': [ 'editorial', ], }) response = client.post(reverse('wiki.edit_document', args=[doc.slug]), data) # Ensure only one of the tags' warning boxes appears, now. response = client.get(reverse('wiki.document', args=[doc.slug]), data) page = pq(response.content) eq_(0, page.find('.warning.review-technical').length) eq_(1, page.find('.warning.review-editorial').length) # Ensure the page appears on the listing pages response = client.get(reverse('wiki.list_review')) eq_( 1, pq(response.content).find("ul.documents li a:contains('%s')" % doc.title).length) response = client.get( reverse('wiki.list_review_tag', args=('technical', ))) eq_( 0, pq(response.content).find("ul.documents li a:contains('%s')" % doc.title).length) response = client.get( reverse('wiki.list_review_tag', args=('editorial', ))) eq_( 1, pq(response.content).find("ul.documents li a:contains('%s')" % doc.title).length) # Also, ensure that the page appears in the proper feeds # HACK: Too lazy to parse the XML. Lazy lazy. response = client.get( reverse('wiki.feeds.list_review', args=('atom', ))) ok_('<entry><title>%s</title>' % doc.title in response.content) response = client.get( reverse('wiki.feeds.list_review_tag', args=( 'atom', 'technical', ))) ok_('<entry><title>%s</title>' % doc.title not in response.content) response = client.get( reverse('wiki.feeds.list_review_tag', args=( 'atom', 'editorial', ))) ok_('<entry><title>%s</title>' % doc.title in response.content)