def test_volume_export(self, mockrepo): mockobj = NonCallableMock() mockobj.pid = 'vol:1' mockobj.title = 'Lecoq, the detective' mockobj.volume = 'V.1' mockobj.date = ['1801'] mockrepo.return_value.get_object.return_value = mockobj # to support for last modified conditional mockobj.ocr.created = datetime.now() # anonymous export_url = reverse('books:webexport', kwargs={'pid': mockobj.pid}) response = self.client.get(export_url) self.assertContains( response, '''<div class="alert alert-warning">Export functionality is only available to logged in users.</div>''', msg_prefix= 'Anonymous user should see warning when viewing export page', html=True) # log in as a regular user self.client.login(**self.user_credentials['user']) response = self.client.get(export_url) self.assert_( 'export_form' in response.context, 'export form should be set in response context for logged in user') self.assertContains( response, 'Export to GitHub requires a GitHub account.', msg_prefix='user should see a warning about github account')
def test_volume_export(self, mockrepo): mockobj = NonCallableMock() mockobj.pid = 'vol:1' mockobj.title = 'Lecoq, the detective' mockobj.volume = 'V.1' mockobj.date = ['1801'] mockrepo.return_value.get_object.return_value = mockobj # to support for last modified conditional mockobj.ocr.created = datetime.now() # anonymous export_url = reverse('books:webexport', kwargs={'pid': mockobj.pid}) response = self.client.get(export_url) self.assertContains(response, '''<div class="alert alert-warning">Export functionality is only available to logged in users.</div>''', msg_prefix='Anonymous user should see warning when viewing export page', html=True) # log in as a regular user self.client.login(**self.user_credentials['user']) response = self.client.get(export_url) self.assert_('export_form' in response.context, 'export form should be set in response context for logged in user') self.assertContains(response, 'Export to GitHub requires a GitHub account.', msg_prefix='user should see a warning about github account')
def test_volume_pages(self, mockpaginator, mockrepo): mockvol = NonCallableMock(spec=Volume) mockvol.pid = 'vol:1' mockvol.title = 'Lecoq, the detective' mockvol.date = ['1801'] # second object retrieved from fedora is page, for layout mockvol.width = 150 mockvol.height = 200 # volume url needed to identify annotations for pages in this volume mockvol.get_absolute_url.return_value = reverse( 'books:volume', kwargs={'pid': mockvol.pid}) mockrepo.return_value.get_object.return_value = mockvol mockvol.find_solr_pages = MagicMock() mockvol.find_solr_pages.return_value.count = 3 mockvol.find_solr_pages.__len__.return_value = 3 mockpage = Mock(width=640, height=400) mockvol.pages = [mockpage] vol_page_url = reverse('books:pages', kwargs={'pid': mockvol.pid}) response = self.client.get(vol_page_url) # volume method should be used to find pages self.assert_(call() in mockvol.find_solr_pages.call_args_list) # volume should be set in context self.assert_(mockvol, response.context['vol']) # annotated pages should be empty for anonymous user self.assertEqual({}, response.context['annotated_pages']) # log in as a regular user self.client.login(**self.user_credentials['user']) testuser = get_user_model().objects.get( username=self.user_credentials['user']['username']) page1_url = reverse('books:page', kwargs={ 'vol_pid': mockvol.pid, 'pid': 'page:1' }) page2_url = reverse('books:page', kwargs={ 'vol_pid': mockvol.pid, 'pid': 'page:2' }) page3_url = reverse('books:page', kwargs={ 'vol_pid': mockvol.pid, 'pid': 'page:3' }) mockvol.page_annotation_count.return_value = { absolutize_url(page1_url): 5, absolutize_url(page2_url): 2, page3_url: 13 } response = self.client.get(vol_page_url) mockvol.page_annotation_count.assert_called_with(testuser) annotated_pages = response.context['annotated_pages'] # counts should be preserved; urls should be non-absolute # whether they started that way or not self.assertEqual(5, annotated_pages[absolutize_url(page1_url)]) self.assertEqual(2, annotated_pages[absolutize_url(page2_url)]) self.assertEqual(13, annotated_pages[page3_url])
def test_volume_pages(self, mockpaginator, mockrepo): mockvol = NonCallableMock(spec=Volume) mockvol.pid = 'vol:1' mockvol.title = 'Lecoq, the detective' mockvol.date = ['1801'] # second object retrieved from fedora is page, for layout mockvol.width = 150 mockvol.height = 200 # volume url needed to identify annotations for pages in this volume mockvol.get_absolute_url.return_value = reverse('books:volume', kwargs={'pid': mockvol.pid}) mockrepo.return_value.get_object.return_value = mockvol mockvol.find_solr_pages = MagicMock() mockvol.find_solr_pages.return_value.count = 3 mockvol.find_solr_pages.__len__.return_value = 3 mockpage = Mock(width=640, height=400) mockvol.pages = [mockpage] vol_page_url = reverse('books:pages', kwargs={'pid': mockvol.pid}) response = self.client.get(vol_page_url) # volume method should be used to find pages self.assert_(call() in mockvol.find_solr_pages.call_args_list) # volume should be set in context self.assert_(mockvol, response.context['vol']) # annotated pages should be empty for anonymous user self.assertEqual({}, response.context['annotated_pages']) # log in as a regular user self.client.login(**self.user_credentials['user']) testuser = get_user_model().objects.get(username=self.user_credentials['user']['username']) page1_url = reverse('books:page', kwargs={'vol_pid': mockvol.pid, 'pid': 'page:1'}) page2_url = reverse('books:page', kwargs={'vol_pid': mockvol.pid, 'pid': 'page:2'}) page3_url = reverse('books:page', kwargs={'vol_pid': mockvol.pid, 'pid': 'page:3'}) mockvol.page_annotation_count.return_value = { absolutize_url(page1_url): 5, absolutize_url(page2_url): 2, page3_url: 13 } response = self.client.get(vol_page_url) mockvol.page_annotation_count.assert_called_with(testuser) annotated_pages = response.context['annotated_pages'] # counts should be preserved; urls should be non-absolute # whether they started that way or not self.assertEqual(5, annotated_pages[absolutize_url(page1_url)]) self.assertEqual(2, annotated_pages[absolutize_url(page2_url)]) self.assertEqual(13, annotated_pages[page3_url])
def test_volume_page_search(self, mocksolr_interface, mockpaginator, mockrepo): mockobj = NonCallableMock() mockobj.pid = 'vol:1' mockobj.title = 'Lecoq, the detective' mockobj.date = ['1801'] mockrepo.return_value.get_object.return_value = mockobj mocksolr = mocksolr_interface.return_value # simulate sunburnt's fluid interface mocksolr.query.return_value = mocksolr.query for method in [ 'query', 'facet_by', 'sort_by', 'field_limit', 'highlight', 'exclude', 'filter', 'join', 'paginate', 'results_as' ]: getattr(mocksolr.query, method).return_value = mocksolr.query # set up mock results for collection query and facet counts solr_result = NonCallableMagicMock( spec_set=['__iter__', 'facet_counts']) # *only* mock iter, to avoid weirdness with django templates & callables solr_results = [ SolrPage( **{ 'pid': 'page:1', 'page_order': '1', 'score': 0.5, 'solr_highlights': { 'page_text': ['snippet with search term'] }, 'identifier': ['http://testpid.co/ark:/1234/11/'] }), SolrPage( **{ 'pid': 'page:233', 'page_order': '123', 'score': 0.02, 'solr_highlights': { 'page_text': ['sample text result from content'] }, 'identifier': ['http://testpid.co/ark:/1234/22/'] }), ] solr_result.__iter__.return_value = solr_results mocksolr.query.__iter__.return_value = iter(solr_result) mocksolr.count.return_value = 2 mockpage = NonCallableMock() mockpaginator.return_value.page.return_value = mockpage results = NonCallableMagicMock( spec=['__iter__', 'facet_counts', 'highlighting']) results.__iter__.return_value = iter(solr_result) mockpage.object_list = results mockpage.has_other_pages = False mockpage.paginator.count = 2 mockpage.paginator.page_range = [1] # patch in highlighting - apparent change in sunburnt behavior results.highlighting = { 'page:1': { 'page_text': ['snippet with search term'] }, 'page:233': { 'page_text': ['sample text result from content'] } } vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid}) response = self.client.get(vol_url, {'keyword': 'determine'}) self.assertEqual( response.templates[0].name, views.VolumeDetail.search_template_name, 'volume search template should be used for valid search submission' ) for page in iter(solr_result): self.assertContains( response, reverse('books:page-image', kwargs={ 'vol_pid': mockobj.pid, 'pid': page.pid, 'mode': 'mini-thumbnail' }), msg_prefix= 'search results should include mini page thumbnail url') self.assertContains( response, "Page %(page_order)s" % page, msg_prefix='search results should include page number') self.assertContains( response, page['score'], msg_prefix='search results should display page relevance score' ) self.assertContains( response, reverse('books:page', kwargs={ 'vol_pid': mockobj.pid, 'pid': page['pid'] }), msg_prefix='search results should link to full page view') self.assertContains( response, '... %s ...' % page['solr_highlights']['page_text'][0], msg_prefix='solr snippets should display when available') # ajax request with patch('readux.books.views.VolumeDetail.get_context_data' ) as mock_ctx: results = NonCallableMagicMock( spec=['__iter__', 'facet_counts', 'highlighting']) results.__iter__.return_value = iter(solr_result) results.highlighting = { solr_results[0].pid: { 'page_text': 'sample highlighting snippet' }, solr_results[1].pid: { 'page_text': 'another highlighting snippet' } } mockpage = NonCallableMagicMock(spec=['__iter__']) mockpage.object_list = results mock_ctx.return_value = { 'pages': mockpage, } response = self.client.get(vol_url, {'keyword': 'determine'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual('application/json', response['content-type']) data = json.loads(response.content) for idx in range(len(data)): self.assertEqual(solr_results[idx].pid, data[idx]['pid']) self.assertEqual('p. %s' % solr_results[idx]['page_order'], data[idx]['label']) self.assertEqual( reverse('books:page-image', kwargs={ 'vol_pid': mockobj.pid, 'pid': solr_results[idx].pid, 'mode': 'mini-thumbnail' }), data[idx]['thumbnail']) self.assertEqual( results.highlighting[solr_results[idx].pid]['page_text'], data[idx]['highlights'])
def test_volume(self, mockrepo): mockobj = NonCallableMock() mockobj.pid = 'vol:1' mockobj.title = 'Lecoq, the detective' mockobj.volume = 'V.1' mockobj.date = ['1801'] mockobj.creator = ['Gaboriau, Emile'] mockobj.book.dc.content.description_list = [ 'Translation of: Monsieur Lecoq.', 'Victorian yellowbacks + paperbacks, 1849-1905' ] mockobj.book.dc.content.publisher = 'London : Vizetelly' mockobj.book.volume_set = [mockobj, NonCallableMock(pid='vol:2')] mockobj.pdf_size = 1024 mockobj.has_pages = False mockobj.is_a_volume = True mockrepo.return_value.get_object.return_value = mockobj # to support for last modified conditional mockobj.ocr.created = datetime.now() vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid}) response = self.client.get(vol_url) self.assertContains(response, mockobj.title, msg_prefix='response should include title') self.assertContains(response, mockobj.volume, msg_prefix='response should include volume label') self.assertContains(response, mockobj.date[0], msg_prefix='response should include date') self.assertContains(response, mockobj.creator[0], msg_prefix='response should include creator') for desc in mockobj.book.dc.content.description_list: self.assertContains( response, desc, msg_prefix='response should include dc:description') self.assertContains(response, mockobj.book.dc.content.publisher, msg_prefix='response should include publisher') self.assertContains(response, reverse('books:pdf', kwargs={'pid': mockobj.pid}), msg_prefix='response should include link to pdf') # related volumes self.assertContains( response, 'Related volumes', msg_prefix='response should include related volumes when present') self.assertContains( response, reverse('books:volume', kwargs={'pid': mockobj.book.volume_set[0].pid}), msg_prefix='response should link to related volumes') # pdf size self.assertContains( response, filesizeformat(mockobj.pdf_size), msg_prefix='PDF size should be displayed in human-readable format') # no pages loaded, should not include volume search or read online self.assertNotContains( response, 'Read online', msg_prefix= 'volume without pages loaded should not display read online option' ) # NOTE: href needed to differentiate from cover url, which starts the same self.assertNotContains( response, 'href="%s"' % reverse('books:pages', kwargs={'pid': mockobj.pid}), msg_prefix= 'volume without pages loaded should not have link to read online') self.assertNotContains( response, '<form id="volume-search" ', msg_prefix= 'volume without pages loaded should not have volume search') # annotation total passed to context self.assert_( 'annotated_volumes' not in response.context, 'annotation count should not be set for volumes without pages') # simulate volume with pages loaded mockobj.has_pages = True # to test annotation count mockobj.get_absolute_url.return_value = '/books/vol:1/' mockobj.annotation_count.return_value = 5 response = self.client.get(vol_url) # *should* include volume search and read online self.assertContains( response, 'Read online', msg_prefix= 'volume with pages loaded should display read online option') self.assertContains( response, reverse('books:pages', kwargs={'pid': mockobj.pid}), msg_prefix= 'volume with pages loaded should have link to read online') self.assertContains( response, '<form id="volume-search" ', msg_prefix='volume without pages loaded should have volume search') # annotation total passed to context self.assertEqual( {mockobj.get_absolute_url(): 5}, response.context['annotated_volumes'], 'annotation count should be set for volumes with pages') mockobj.annotation_count.return_value = 0 response = self.client.get(vol_url) self.assert_( 'annotated_volumes' not in response.context, 'annotation count should not be set in context when it is zero') # non-existent should 404 mockobj.exists = False response = self.client.get(vol_url) expected, got = 404, response.status_code self.assertEqual(expected, got, 'expected %s for %s when object does not exist, got %s' % \ (expected, vol_url, got)) # exists but isn't a volume - should also 404 mockobj.exists = True mockobj.is_a_volume = False response = self.client.get(vol_url) expected, got = 404, response.status_code self.assertEqual(expected, got, 'expected %s for %s when object is not a volume, got %s' % \ (expected, vol_url, got))
def test_volume_page_search(self, mocksolr_interface, mockpaginator, mockrepo): mockobj = NonCallableMock() mockobj.pid = 'vol:1' mockobj.title = 'Lecoq, the detective' mockobj.date = ['1801'] mockrepo.return_value.get_object.return_value = mockobj mocksolr = mocksolr_interface.return_value # simulate sunburnt's fluid interface mocksolr.query.return_value = mocksolr.query for method in ['query', 'facet_by', 'sort_by', 'field_limit', 'highlight', 'exclude', 'filter', 'join', 'paginate', 'results_as']: getattr(mocksolr.query, method).return_value = mocksolr.query # set up mock results for collection query and facet counts solr_result = NonCallableMagicMock(spec_set=['__iter__', 'facet_counts']) # *only* mock iter, to avoid weirdness with django templates & callables solr_results = [ SolrPage(**{'pid': 'page:1', 'page_order': '1', 'score': 0.5, 'solr_highlights': {'page_text': ['snippet with search term']}, 'identifier': ['http://testpid.co/ark:/1234/11/']}), SolrPage(**{'pid': 'page:233', 'page_order': '123', 'score': 0.02, 'solr_highlights': {'page_text': ['sample text result from content']}, 'identifier': ['http://testpid.co/ark:/1234/22/']}), ] solr_result.__iter__.return_value = solr_results mocksolr.query.__iter__.return_value = iter(solr_result) mocksolr.count.return_value = 2 mockpage = NonCallableMock() mockpaginator.return_value.page.return_value = mockpage results = NonCallableMagicMock(spec=['__iter__', 'facet_counts', 'highlighting']) results.__iter__.return_value = iter(solr_result) mockpage.object_list = results mockpage.has_other_pages = False mockpage.paginator.count = 2 mockpage.paginator.page_range = [1] # patch in highlighting - apparent change in sunburnt behavior results.highlighting = { 'page:1': {'page_text': ['snippet with search term']}, 'page:233': {'page_text': ['sample text result from content']} } vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid}) response = self.client.get(vol_url, {'keyword': 'determine'}) self.assertEqual(response.templates[0].name, views.VolumeDetail.search_template_name, 'volume search template should be used for valid search submission') for page in iter(solr_result): self.assertContains(response, reverse('books:page-image', kwargs={'vol_pid': mockobj.pid, 'pid': page.pid, 'mode': 'mini-thumbnail'}), msg_prefix='search results should include mini page thumbnail url') self.assertContains(response, "Page %(page_order)s" % page, msg_prefix='search results should include page number') self.assertContains(response, page['score'], msg_prefix='search results should display page relevance score') self.assertContains(response, reverse('books:page', kwargs={'vol_pid': mockobj.pid, 'pid': page['pid']}), msg_prefix='search results should link to full page view') self.assertContains(response, '... %s ...' % page['solr_highlights']['page_text'][0], msg_prefix='solr snippets should display when available') # ajax request with patch('readux.books.views.VolumeDetail.get_context_data') as mock_ctx: results = NonCallableMagicMock(spec=['__iter__', 'facet_counts', 'highlighting']) results.__iter__.return_value = iter(solr_result) results.highlighting = { solr_results[0].pid: { 'page_text': 'sample highlighting snippet' }, solr_results[1].pid: { 'page_text': 'another highlighting snippet' } } mockpage = NonCallableMagicMock(spec=['__iter__']) mockpage.object_list = results mock_ctx.return_value = { 'pages': mockpage, } response = self.client.get(vol_url, {'keyword': 'determine'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual('application/json', response['content-type']) data = json.loads(response.content) for idx in range(len(data)): self.assertEqual(solr_results[idx].pid, data[idx]['pid']) self.assertEqual('p. %s' % solr_results[idx]['page_order'], data[idx]['label']) self.assertEqual(reverse('books:page-image', kwargs={'vol_pid': mockobj.pid, 'pid': solr_results[idx].pid, 'mode': 'mini-thumbnail'}), data[idx]['thumbnail']) self.assertEqual(results.highlighting[solr_results[idx].pid]['page_text'], data[idx]['highlights'])
def test_volume(self, mockrepo): mockobj = NonCallableMock() mockobj.pid = 'vol:1' mockobj.title = 'Lecoq, the detective' mockobj.volume = 'V.1' mockobj.date = ['1801'] mockobj.creator = ['Gaboriau, Emile'] mockobj.book.dc.content.description_list = [ 'Translation of: Monsieur Lecoq.', 'Victorian yellowbacks + paperbacks, 1849-1905' ] mockobj.book.dc.content.publisher = 'London : Vizetelly' mockobj.book.volume_set = [mockobj, NonCallableMock(pid='vol:2')] mockobj.pdf_size = 1024 mockobj.has_pages = False mockobj.is_a_volume = True mockrepo.return_value.get_object.return_value = mockobj # to support for last modified conditional mockobj.ocr.created = datetime.now() vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid}) response = self.client.get(vol_url) self.assertContains(response, mockobj.title, msg_prefix='response should include title') self.assertContains(response, mockobj.volume, msg_prefix='response should include volume label') self.assertContains(response, mockobj.date[0], msg_prefix='response should include date') self.assertContains(response, mockobj.creator[0], msg_prefix='response should include creator') for desc in mockobj.book.dc.content.description_list: self.assertContains(response, desc, msg_prefix='response should include dc:description') self.assertContains(response, mockobj.book.dc.content.publisher, msg_prefix='response should include publisher') self.assertContains(response, reverse('books:pdf', kwargs={'pid': mockobj.pid}), msg_prefix='response should include link to pdf') # related volumes self.assertContains(response, 'Related volumes', msg_prefix='response should include related volumes when present') self.assertContains(response, reverse('books:volume', kwargs={'pid': mockobj.book.volume_set[0].pid}), msg_prefix='response should link to related volumes') # pdf size self.assertContains(response, filesizeformat(mockobj.pdf_size), msg_prefix='PDF size should be displayed in human-readable format') # no pages loaded, should not include volume search or read online self.assertNotContains(response, 'Read online', msg_prefix='volume without pages loaded should not display read online option') # NOTE: href needed to differentiate from cover url, which starts the same self.assertNotContains(response, 'href="%s"' % reverse('books:pages', kwargs={'pid': mockobj.pid}), msg_prefix='volume without pages loaded should not have link to read online') self.assertNotContains(response, '<form id="volume-search" ', msg_prefix='volume without pages loaded should not have volume search') # annotation total passed to context self.assert_('annotated_volumes' not in response.context, 'annotation count should not be set for volumes without pages') # simulate volume with pages loaded mockobj.has_pages = True # to test annotation count mockobj.get_absolute_url.return_value = '/books/vol:1/' mockobj.annotation_count.return_value = 5 response = self.client.get(vol_url) # *should* include volume search and read online self.assertContains(response, 'Read online', msg_prefix='volume with pages loaded should display read online option') self.assertContains(response, reverse('books:pages', kwargs={'pid': mockobj.pid}), msg_prefix='volume with pages loaded should have link to read online') self.assertContains(response, '<form id="volume-search" ', msg_prefix='volume without pages loaded should have volume search') # annotation total passed to context self.assertEqual({mockobj.get_absolute_url(): 5}, response.context['annotated_volumes'], 'annotation count should be set for volumes with pages') mockobj.annotation_count.return_value = 0 response = self.client.get(vol_url) self.assert_('annotated_volumes' not in response.context, 'annotation count should not be set in context when it is zero') # non-existent should 404 mockobj.exists = False response = self.client.get(vol_url) expected, got = 404, response.status_code self.assertEqual(expected, got, 'expected %s for %s when object does not exist, got %s' % \ (expected, vol_url, got)) # exists but isn't a volume - should also 404 mockobj.exists = True mockobj.is_a_volume = False response = self.client.get(vol_url) expected, got = 404, response.status_code self.assertEqual(expected, got, 'expected %s for %s when object is not a volume, got %s' % \ (expected, vol_url, got))