Commit 9b0fa006 by Ned Batchelder

Merge pull request #311 from edx/ned/staticbook-tests

Ned/staticbook tests
parents 7e158b2f f6b0ffd0
...@@ -19,14 +19,13 @@ class XModuleCourseFactory(Factory): ...@@ -19,14 +19,13 @@ class XModuleCourseFactory(Factory):
ABSTRACT_FACTORY = True ABSTRACT_FACTORY = True
@classmethod @classmethod
def _create(cls, target_class, *args, **kwargs): def _create(cls, target_class, **kwargs):
template = Location('i4x', 'edx', 'templates', 'course', 'Empty') template = Location('i4x', 'edx', 'templates', 'course', 'Empty')
org = kwargs.get('org') org = kwargs.pop('org', None)
number = kwargs.get('number') number = kwargs.pop('number', None)
display_name = kwargs.get('display_name') display_name = kwargs.pop('display_name', None)
location = Location('i4x', org, number, location = Location('i4x', org, number, 'course', Location.clean(display_name))
'course', Location.clean(display_name))
try: try:
store = modulestore('direct') store = modulestore('direct')
...@@ -41,7 +40,7 @@ class XModuleCourseFactory(Factory): ...@@ -41,7 +40,7 @@ class XModuleCourseFactory(Factory):
new_course.display_name = display_name new_course.display_name = display_name
new_course.lms.start = datetime.datetime.now(UTC) new_course.lms.start = datetime.datetime.now(UTC)
new_course.tabs = kwargs.get( new_course.tabs = kwargs.pop(
'tabs', 'tabs',
[ [
{"type": "courseware"}, {"type": "courseware"},
...@@ -51,15 +50,18 @@ class XModuleCourseFactory(Factory): ...@@ -51,15 +50,18 @@ class XModuleCourseFactory(Factory):
{"type": "progress", "name": "Progress"} {"type": "progress", "name": "Progress"}
] ]
) )
new_course.discussion_link = kwargs.get('discussion_link')
# Update the data in the mongo datastore data = kwargs.pop('data', None)
store.update_metadata(new_course.location.url(), own_metadata(new_course))
data = kwargs.get('data')
if data is not None: if data is not None:
store.update_item(new_course.location, data) store.update_item(new_course.location, data)
# The rest of kwargs become attributes on the course:
for k, v in kwargs.iteritems():
setattr(new_course, k, v)
# Update the data in the mongo datastore
store.update_metadata(new_course.location.url(), own_metadata(new_course))
# update_item updates the the course as it exists in the modulestore, but doesn't # update_item updates the the course as it exists in the modulestore, but doesn't
# update the instance we are working with, so have to refetch the course after updating it. # update the instance we are working with, so have to refetch the course after updating it.
new_course = store.get_instance(new_course.id, new_course.location) new_course = store.get_instance(new_course.id, new_course.location)
...@@ -101,7 +103,7 @@ class XModuleItemFactory(Factory): ...@@ -101,7 +103,7 @@ class XModuleItemFactory(Factory):
return parent._replace(category=attr.category, name=dest_name) return parent._replace(category=attr.category, name=dest_name)
@classmethod @classmethod
def _create(cls, target_class, *args, **kwargs): def _create(cls, target_class, **kwargs):
""" """
Uses *kwargs*: Uses *kwargs*:
......
"""
Test the lms/staticbook views.
"""
from django.test.utils import override_settings
from django.core.urlresolvers import reverse
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
PDF_BOOK = {
"tab_title": "Textbook",
"title": "A PDF Textbook",
"chapters": [
{ "title": "Chapter 1 for PDF", "url": "https://somehost.com/the_book/chap1.pdf" },
{ "title": "Chapter 2 for PDF", "url": "https://somehost.com/the_book/chap2.pdf" },
],
}
HTML_BOOK = {
"tab_title": "Textbook",
"title": "An HTML Textbook",
"chapters": [
{ "title": "Chapter 1 for HTML", "url": "https://somehost.com/the_book/chap1.html" },
{ "title": "Chapter 2 for HTML", "url": "https://somehost.com/the_book/chap2.html" },
],
}
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class StaticBookTest(ModuleStoreTestCase):
"""
Helpers for the static book tests.
"""
def __init__(self, *args, **kwargs):
super(StaticBookTest, self).__init__(*args, **kwargs)
self.course = None
def make_course(self, **kwargs):
"""
Make a course with an enrolled logged-in student.
"""
self.course = CourseFactory.create(**kwargs)
user = UserFactory.create()
CourseEnrollmentFactory.create(user=user, course_id=self.course.id)
self.client.login(username=user.username, password='test')
def make_url(self, url_name, **kwargs):
"""
Make a URL for a `url_name` using keyword args for url slots.
Automatically provides the course id.
"""
kwargs['course_id'] = self.course.id
url = reverse(url_name, kwargs=kwargs)
return url
class StaticPdfBookTest(StaticBookTest):
"""
Test the PDF static book view.
"""
def test_book(self):
# We can access a book.
self.make_course(pdf_textbooks=[PDF_BOOK])
url = self.make_url('pdf_book', book_index=0)
response = self.client.get(url)
self.assertContains(response, "Chapter 1 for PDF")
self.assertNotContains(response, "options.chapterNum =")
self.assertNotContains(response, "options.pageNum =")
def test_book_chapter(self):
# We can access a book at a particular chapter.
self.make_course(pdf_textbooks=[PDF_BOOK])
url = self.make_url('pdf_book', book_index=0, chapter=2)
response = self.client.get(url)
self.assertContains(response, "Chapter 2 for PDF")
self.assertContains(response, "options.chapterNum = 2;")
self.assertNotContains(response, "options.pageNum =")
def test_book_page(self):
# We can access a book at a particular page.
self.make_course(pdf_textbooks=[PDF_BOOK])
url = self.make_url('pdf_book', book_index=0, page=17)
response = self.client.get(url)
self.assertContains(response, "Chapter 1 for PDF")
self.assertNotContains(response, "options.chapterNum =")
self.assertContains(response, "options.pageNum = 17;")
def test_book_chapter_page(self):
# We can access a book at a particular chapter and page.
self.make_course(pdf_textbooks=[PDF_BOOK])
url = self.make_url('pdf_book', book_index=0, chapter=2, page=17)
response = self.client.get(url)
self.assertContains(response, "Chapter 2 for PDF")
self.assertContains(response, "options.chapterNum = 2;")
self.assertContains(response, "options.pageNum = 17;")
def test_bad_book_id(self):
# If we have one book, asking for the second book will fail with a 404.
self.make_course(pdf_textbooks=[PDF_BOOK])
url = self.make_url('pdf_book', book_index=1, chapter=1)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_no_book(self):
# If we have no books, asking for the first book will fail with a 404.
self.make_course()
url = self.make_url('pdf_book', book_index=0, chapter=1)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
class StaticHtmlBookTest(StaticBookTest):
"""
Test the HTML static book view.
"""
def test_book(self):
# We can access a book.
self.make_course(html_textbooks=[HTML_BOOK])
url = self.make_url('html_book', book_index=0)
response = self.client.get(url)
self.assertContains(response, "Chapter 1 for HTML")
self.assertNotContains(response, "options.chapterNum =")
def test_book_chapter(self):
# We can access a book at a particular chapter.
self.make_course(html_textbooks=[HTML_BOOK])
url = self.make_url('html_book', book_index=0, chapter=2)
response = self.client.get(url)
self.assertContains(response, "Chapter 2 for HTML")
self.assertContains(response, "options.chapterNum = 2;")
def test_bad_book_id(self):
# If we have one book, asking for the second book will fail with a 404.
self.make_course(html_textbooks=[HTML_BOOK])
url = self.make_url('html_book', book_index=1, chapter=1)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_no_book(self):
# If we have no books, asking for the first book will fail with a 404.
self.make_course()
url = self.make_url('html_book', book_index=0, chapter=1)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
"""
Views for serving static textbooks.
"""
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import Http404 from django.http import Http404
from mitxmako.shortcuts import render_to_response from mitxmako.shortcuts import render_to_response
...@@ -10,6 +14,9 @@ from static_replace import replace_static_urls ...@@ -10,6 +14,9 @@ from static_replace import replace_static_urls
@login_required @login_required
def index(request, course_id, book_index, page=None): def index(request, course_id, book_index, page=None):
"""
Serve static image-based textbooks.
"""
course = get_course_with_access(request.user, course_id, 'load') course = get_course_with_access(request.user, course_id, 'load')
staff_access = has_access(request.user, course, 'staff') staff_access = has_access(request.user, course, 'staff')
...@@ -22,18 +29,31 @@ def index(request, course_id, book_index, page=None): ...@@ -22,18 +29,31 @@ def index(request, course_id, book_index, page=None):
if page is None: if page is None:
page = textbook.start_page page = textbook.start_page
return render_to_response('staticbook.html', return render_to_response(
{'book_index': book_index, 'page': int(page), 'staticbook.html',
'course': course, {
'book_url': textbook.book_url, 'book_index': book_index, 'page': int(page),
'table_of_contents': table_of_contents, 'course': course,
'start_page': textbook.start_page, 'book_url': textbook.book_url,
'end_page': textbook.end_page, 'table_of_contents': table_of_contents,
'staff_access': staff_access}) 'start_page': textbook.start_page,
'end_page': textbook.end_page,
'staff_access': staff_access,
def index_shifted(request, course_id, page): },
return index(request, course_id=course_id, page=int(page) + 24) )
def remap_static_url(original_url, course):
"""Remap a URL in the ways the course requires."""
# Ick: this should be possible without having to quote and unquote the URL...
input_url = "'" + original_url + "'"
output_url = replace_static_urls(
input_url,
getattr(course, 'data_dir', None),
course_namespace=course.location,
)
# strip off the quotes again...
return output_url[1:-1]
@login_required @login_required
...@@ -60,16 +80,6 @@ def pdf_index(request, course_id, book_index, chapter=None, page=None): ...@@ -60,16 +80,6 @@ def pdf_index(request, course_id, book_index, chapter=None, page=None):
raise Http404("Invalid book index value: {0}".format(book_index)) raise Http404("Invalid book index value: {0}".format(book_index))
textbook = course.pdf_textbooks[book_index] textbook = course.pdf_textbooks[book_index]
def remap_static_url(original_url, course):
input_url = "'" + original_url + "'"
output_url = replace_static_urls(
input_url,
getattr(course, 'data_dir', None),
course_namespace=course.location
)
# strip off the quotes again...
return output_url[1:-1]
if 'url' in textbook: if 'url' in textbook:
textbook['url'] = remap_static_url(textbook['url'], course) textbook['url'] = remap_static_url(textbook['url'], course)
# then remap all the chapter URLs as well, if they are provided. # then remap all the chapter URLs as well, if they are provided.
...@@ -77,13 +87,17 @@ def pdf_index(request, course_id, book_index, chapter=None, page=None): ...@@ -77,13 +87,17 @@ def pdf_index(request, course_id, book_index, chapter=None, page=None):
for entry in textbook['chapters']: for entry in textbook['chapters']:
entry['url'] = remap_static_url(entry['url'], course) entry['url'] = remap_static_url(entry['url'], course)
return render_to_response('static_pdfbook.html', return render_to_response(
{'book_index': book_index, 'static_pdfbook.html',
'course': course, {
'textbook': textbook, 'book_index': book_index,
'chapter': chapter, 'course': course,
'page': page, 'textbook': textbook,
'staff_access': staff_access}) 'chapter': chapter,
'page': page,
'staff_access': staff_access,
},
)
@login_required @login_required
...@@ -109,16 +123,6 @@ def html_index(request, course_id, book_index, chapter=None): ...@@ -109,16 +123,6 @@ def html_index(request, course_id, book_index, chapter=None):
raise Http404("Invalid book index value: {0}".format(book_index)) raise Http404("Invalid book index value: {0}".format(book_index))
textbook = course.html_textbooks[book_index] textbook = course.html_textbooks[book_index]
def remap_static_url(original_url, course):
input_url = "'" + original_url + "'"
output_url = replace_static_urls(
input_url,
getattr(course, 'data_dir', None),
course_namespace=course.location
)
# strip off the quotes again...
return output_url[1:-1]
if 'url' in textbook: if 'url' in textbook:
textbook['url'] = remap_static_url(textbook['url'], course) textbook['url'] = remap_static_url(textbook['url'], course)
# then remap all the chapter URLs as well, if they are provided. # then remap all the chapter URLs as well, if they are provided.
...@@ -126,10 +130,14 @@ def html_index(request, course_id, book_index, chapter=None): ...@@ -126,10 +130,14 @@ def html_index(request, course_id, book_index, chapter=None):
for entry in textbook['chapters']: for entry in textbook['chapters']:
entry['url'] = remap_static_url(entry['url'], course) entry['url'] = remap_static_url(entry['url'], course)
return render_to_response('static_htmlbook.html', return render_to_response(
{'book_index': book_index, 'static_htmlbook.html',
'course': course, {
'textbook': textbook, 'book_index': book_index,
'chapter': chapter, 'course': course,
'staff_access': staff_access, 'textbook': textbook,
'notes_enabled': notes_enabled}) 'chapter': chapter,
'staff_access': staff_access,
'notes_enabled': notes_enabled,
},
)
...@@ -227,23 +227,21 @@ if settings.COURSEWARE_ENABLED: ...@@ -227,23 +227,21 @@ if settings.COURSEWARE_ENABLED:
'staticbook.views.index', name="book"), 'staticbook.views.index', name="book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/book/(?P<book_index>[^/]*)/(?P<page>[^/]*)$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/book/(?P<book_index>[^/]*)/(?P<page>[^/]*)$',
'staticbook.views.index'), 'staticbook.views.index'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/book-shifted/(?P<page>[^/]*)$',
'staticbook.views.index_shifted'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/$',
'staticbook.views.pdf_index', name="pdf_book"), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/(?P<page>[^/]*)$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/(?P<page>[^/]*)$',
'staticbook.views.pdf_index'), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/$',
'staticbook.views.pdf_index'), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/(?P<page>[^/]*)$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/pdfbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/(?P<page>[^/]*)$',
'staticbook.views.pdf_index'), 'staticbook.views.pdf_index', name="pdf_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/$',
'staticbook.views.html_index', name="html_book"), 'staticbook.views.html_index', name="html_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/htmlbook/(?P<book_index>[^/]*)/chapter/(?P<chapter>[^/]*)/$',
'staticbook.views.html_index'), 'staticbook.views.html_index', name="html_book"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/?$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/courseware/?$',
'courseware.views.index', name="courseware"), 'courseware.views.index', name="courseware"),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment