Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
05cb13b1
Commit
05cb13b1
authored
Jun 19, 2013
by
David Baumgold
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding and fixing tests
parent
77db1f8a
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
212 additions
and
107 deletions
+212
-107
cms/djangoapps/contentstore/tests/test_checklists.py
+1
-1
cms/djangoapps/contentstore/tests/test_course_settings.py
+33
-66
cms/djangoapps/contentstore/tests/test_course_updates.py
+25
-25
cms/djangoapps/contentstore/tests/test_item.py
+0
-4
cms/djangoapps/contentstore/tests/test_textbooks.py
+95
-0
cms/djangoapps/contentstore/tests/utils.py
+38
-0
cms/djangoapps/contentstore/views/course.py
+20
-11
cms/manage.py
+0
-0
No files found.
cms/djangoapps/contentstore/tests/test_checklists.py
View file @
05cb13b1
""" Unit tests for checklist methods in views.py. """
from
contentstore.utils
import
get_modulestore
,
get_url_reverse
from
contentstore.tests.test_course_settings
import
CourseTestCase
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
django.core.urlresolvers
import
reverse
import
json
from
.utils
import
CourseTestCase
class
ChecklistTestCase
(
CourseTestCase
):
...
...
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
05cb13b1
...
...
@@ -6,8 +6,6 @@ import json
import
copy
import
mock
from
django.contrib.auth.models
import
User
from
django.test.client
import
Client
from
django.core.urlresolvers
import
reverse
from
django.utils.timezone
import
UTC
from
django.test.utils
import
override_settings
...
...
@@ -17,45 +15,12 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder
from
models.settings.course_grading
import
CourseGradingModel
from
contentstore.utils
import
get_modulestore
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
models.settings.course_metadata
import
CourseMetadata
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
xmodule.fields
import
Date
class
CourseTestCase
(
ModuleStoreTestCase
):
"""
Base class for test classes below.
"""
def
setUp
(
self
):
"""
These tests need a user in the DB so that the django Test Client
can log them in.
They inherit from the ModuleStoreTestCase class so that the mongodb collection
will be cleared out before each test case execution and deleted
afterwards.
"""
uname
=
'testuser'
email
=
'test+courses@edx.org'
password
=
'foo'
# Create the use so we can log them in.
self
.
user
=
User
.
objects
.
create_user
(
uname
,
email
,
password
)
# Note that we do not actually need to do anything
# for registration if we directly mark them active.
self
.
user
.
is_active
=
True
# Staff has access to view all courses
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
self
.
client
=
Client
()
self
.
client
.
login
(
username
=
uname
,
password
=
password
)
course
=
CourseFactory
.
create
(
template
=
'i4x://edx/templates/course/Empty'
,
org
=
'MITx'
,
number
=
'999'
,
display_name
=
'Robot Super Course'
)
self
.
course_location
=
course
.
location
from
.utils
import
CourseTestCase
class
CourseDetailsTestCase
(
CourseTestCase
):
...
...
@@ -63,8 +28,8 @@ class CourseDetailsTestCase(CourseTestCase):
Tests the first course settings page (course dates, overview, etc.).
"""
def
test_virgin_fetch
(
self
):
details
=
CourseDetails
.
fetch
(
self
.
course
_
location
)
self
.
assertEqual
(
details
.
course_location
,
self
.
course
_
location
,
"Location not copied into"
)
details
=
CourseDetails
.
fetch
(
self
.
course
.
location
)
self
.
assertEqual
(
details
.
course_location
,
self
.
course
.
location
,
"Location not copied into"
)
self
.
assertIsNotNone
(
details
.
start_date
.
tzinfo
)
self
.
assertIsNone
(
details
.
end_date
,
"end date somehow initialized "
+
str
(
details
.
end_date
))
self
.
assertIsNone
(
details
.
enrollment_start
,
"enrollment_start date somehow initialized "
+
str
(
details
.
enrollment_start
))
...
...
@@ -75,10 +40,10 @@ class CourseDetailsTestCase(CourseTestCase):
self
.
assertIsNone
(
details
.
effort
,
"effort somehow initialized"
+
str
(
details
.
effort
))
def
test_encoder
(
self
):
details
=
CourseDetails
.
fetch
(
self
.
course
_
location
)
details
=
CourseDetails
.
fetch
(
self
.
course
.
location
)
jsondetails
=
json
.
dumps
(
details
,
cls
=
CourseSettingsEncoder
)
jsondetails
=
json
.
loads
(
jsondetails
)
self
.
assertTupleEqual
(
Location
(
jsondetails
[
'course_location'
]),
self
.
course
_
location
,
"Location !="
)
self
.
assertTupleEqual
(
Location
(
jsondetails
[
'course_location'
]),
self
.
course
.
location
,
"Location !="
)
self
.
assertIsNone
(
jsondetails
[
'end_date'
],
"end date somehow initialized "
)
self
.
assertIsNone
(
jsondetails
[
'enrollment_start'
],
"enrollment_start date somehow initialized "
)
self
.
assertIsNone
(
jsondetails
[
'enrollment_end'
],
"enrollment_end date somehow initialized "
)
...
...
@@ -91,10 +56,12 @@ class CourseDetailsTestCase(CourseTestCase):
"""
Test the encoder out of its original constrained purpose to see if it functions for general use
"""
details
=
{
'location'
:
Location
([
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]),
details
=
{
'location'
:
Location
([
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]),
'number'
:
1
,
'string'
:
'string'
,
'datetime'
:
datetime
.
datetime
.
now
(
UTC
())}
'datetime'
:
datetime
.
datetime
.
now
(
UTC
())
}
jsondetails
=
json
.
dumps
(
details
,
cls
=
CourseSettingsEncoder
)
jsondetails
=
json
.
loads
(
jsondetails
)
...
...
@@ -105,7 +72,7 @@ class CourseDetailsTestCase(CourseTestCase):
self
.
assertEqual
(
jsondetails
[
'string'
],
'string'
)
def
test_update_and_fetch
(
self
):
jsondetails
=
CourseDetails
.
fetch
(
self
.
course
_
location
)
jsondetails
=
CourseDetails
.
fetch
(
self
.
course
.
location
)
jsondetails
.
syllabus
=
"<a href='foo'>bar</a>"
# encode - decode to convert date fields and other data which changes form
self
.
assertEqual
(
...
...
@@ -204,11 +171,12 @@ class CourseDetailsViewTest(CourseTestCase):
return
Date
()
.
to_json
(
dt
)
def
test_update_and_fetch
(
self
):
details
=
CourseDetails
.
fetch
(
self
.
course_location
)
loc
=
self
.
course
.
location
details
=
CourseDetails
.
fetch
(
loc
)
# resp s/b json from here on
url
=
reverse
(
'course_settings'
,
kwargs
=
{
'org'
:
self
.
course_location
.
org
,
'course'
:
self
.
course_location
.
course
,
'name'
:
self
.
course_location
.
name
,
'section'
:
'details'
})
url
=
reverse
(
'course_settings'
,
kwargs
=
{
'org'
:
loc
.
org
,
'course'
:
loc
.
course
,
'name'
:
loc
.
name
,
'section'
:
'details'
})
resp
=
self
.
client
.
get
(
url
)
self
.
compare_details_with_encoding
(
json
.
loads
(
resp
.
content
),
details
.
__dict__
,
"virgin get"
)
...
...
@@ -251,49 +219,49 @@ class CourseGradingTest(CourseTestCase):
Tests for the course settings grading page.
"""
def
test_initial_grader
(
self
):
descriptor
=
get_modulestore
(
self
.
course
_location
)
.
get_item
(
self
.
course_
location
)
descriptor
=
get_modulestore
(
self
.
course
.
location
)
.
get_item
(
self
.
course
.
location
)
test_grader
=
CourseGradingModel
(
descriptor
)
# ??? How much should this test bake in expectations about defaults and thus fail if defaults change?
self
.
assertEqual
(
self
.
course
_
location
,
test_grader
.
course_location
,
"Course locations"
)
self
.
assertEqual
(
self
.
course
.
location
,
test_grader
.
course_location
,
"Course locations"
)
self
.
assertIsNotNone
(
test_grader
.
graders
,
"No graders"
)
self
.
assertIsNotNone
(
test_grader
.
grade_cutoffs
,
"No cutoffs"
)
def
test_fetch_grader
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
_
location
.
url
())
self
.
assertEqual
(
self
.
course
_
location
,
test_grader
.
course_location
,
"Course locations"
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
.
location
.
url
())
self
.
assertEqual
(
self
.
course
.
location
,
test_grader
.
course_location
,
"Course locations"
)
self
.
assertIsNotNone
(
test_grader
.
graders
,
"No graders"
)
self
.
assertIsNotNone
(
test_grader
.
grade_cutoffs
,
"No cutoffs"
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
_
location
)
self
.
assertEqual
(
self
.
course
_
location
,
test_grader
.
course_location
,
"Course locations"
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
.
location
)
self
.
assertEqual
(
self
.
course
.
location
,
test_grader
.
course_location
,
"Course locations"
)
self
.
assertIsNotNone
(
test_grader
.
graders
,
"No graders"
)
self
.
assertIsNotNone
(
test_grader
.
grade_cutoffs
,
"No cutoffs"
)
for
i
,
grader
in
enumerate
(
test_grader
.
graders
):
subgrader
=
CourseGradingModel
.
fetch_grader
(
self
.
course
_
location
,
i
)
subgrader
=
CourseGradingModel
.
fetch_grader
(
self
.
course
.
location
,
i
)
self
.
assertDictEqual
(
grader
,
subgrader
,
str
(
i
)
+
"th graders not equal"
)
subgrader
=
CourseGradingModel
.
fetch_grader
(
self
.
course
_
location
.
list
(),
0
)
subgrader
=
CourseGradingModel
.
fetch_grader
(
self
.
course
.
location
.
list
(),
0
)
self
.
assertDictEqual
(
test_grader
.
graders
[
0
],
subgrader
,
"failed with location as list"
)
def
test_fetch_cutoffs
(
self
):
test_grader
=
CourseGradingModel
.
fetch_cutoffs
(
self
.
course
_
location
)
test_grader
=
CourseGradingModel
.
fetch_cutoffs
(
self
.
course
.
location
)
# ??? should this check that it's at least a dict? (expected is { "pass" : 0.5 } I think)
self
.
assertIsNotNone
(
test_grader
,
"No cutoffs via fetch"
)
test_grader
=
CourseGradingModel
.
fetch_cutoffs
(
self
.
course
_
location
.
url
())
test_grader
=
CourseGradingModel
.
fetch_cutoffs
(
self
.
course
.
location
.
url
())
self
.
assertIsNotNone
(
test_grader
,
"No cutoffs via fetch with url"
)
def
test_fetch_grace
(
self
):
test_grader
=
CourseGradingModel
.
fetch_grace_period
(
self
.
course
_
location
)
test_grader
=
CourseGradingModel
.
fetch_grace_period
(
self
.
course
.
location
)
# almost a worthless test
self
.
assertIn
(
'grace_period'
,
test_grader
,
"No grace via fetch"
)
test_grader
=
CourseGradingModel
.
fetch_grace_period
(
self
.
course
_
location
.
url
())
test_grader
=
CourseGradingModel
.
fetch_grace_period
(
self
.
course
.
location
.
url
())
self
.
assertIn
(
'grace_period'
,
test_grader
,
"No cutoffs via fetch with url"
)
def
test_update_from_json
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
_
location
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
.
location
)
altered_grader
=
CourseGradingModel
.
update_from_json
(
test_grader
.
__dict__
)
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"Noop update"
)
...
...
@@ -307,11 +275,10 @@ class CourseGradingTest(CourseTestCase):
test_grader
.
grace_period
=
{
'hours'
:
4
,
'minutes'
:
5
,
'seconds'
:
0
}
altered_grader
=
CourseGradingModel
.
update_from_json
(
test_grader
.
__dict__
)
print
test_grader
.
grace_period
,
altered_grader
.
grace_period
self
.
assertDictEqual
(
test_grader
.
__dict__
,
altered_grader
.
__dict__
,
"4 hour grace period"
)
def
test_update_grader_from_json
(
self
):
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
_
location
)
test_grader
=
CourseGradingModel
.
fetch
(
self
.
course
.
location
)
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
test_grader
.
course_location
,
test_grader
.
graders
[
1
])
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"Noop update"
)
...
...
@@ -331,11 +298,11 @@ class CourseMetadataEditingTest(CourseTestCase):
def
setUp
(
self
):
CourseTestCase
.
setUp
(
self
)
# add in the full class too
import_from_xml
(
get_modulestore
(
self
.
course
_
location
),
'common/test/data/'
,
[
'full'
])
import_from_xml
(
get_modulestore
(
self
.
course
.
location
),
'common/test/data/'
,
[
'full'
])
self
.
fullcourse_location
=
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
])
def
test_fetch_initial_fields
(
self
):
test_model
=
CourseMetadata
.
fetch
(
self
.
course
_
location
)
test_model
=
CourseMetadata
.
fetch
(
self
.
course
.
location
)
self
.
assertIn
(
'display_name'
,
test_model
,
'Missing editable metadata field'
)
self
.
assertEqual
(
test_model
[
'display_name'
],
'Robot Super Course'
,
"not expected value"
)
...
...
@@ -348,17 +315,17 @@ class CourseMetadataEditingTest(CourseTestCase):
self
.
assertIn
(
'xqa_key'
,
test_model
,
'xqa_key field '
)
def
test_update_from_json
(
self
):
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
_
location
,
{
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
.
location
,
{
"advertised_start"
:
"start A"
,
"testcenter_info"
:
{
"c"
:
"test"
},
"days_early_for_beta"
:
2
})
self
.
update_check
(
test_model
)
# try fresh fetch to ensure persistence
test_model
=
CourseMetadata
.
fetch
(
self
.
course
_
location
)
test_model
=
CourseMetadata
.
fetch
(
self
.
course
.
location
)
self
.
update_check
(
test_model
)
# now change some of the existing metadata
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
_
location
,
{
test_model
=
CourseMetadata
.
update_from_json
(
self
.
course
.
location
,
{
"advertised_start"
:
"start B"
,
"display_name"
:
"jolly roger"
}
)
...
...
cms/djangoapps/contentstore/tests/test_course_updates.py
View file @
05cb13b1
...
...
@@ -10,9 +10,9 @@ class CourseUpdateTest(CourseTestCase):
'''Go through each interface and ensure it works.'''
# first get the update to force the creation
url
=
reverse
(
'course_info'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
'name'
:
self
.
course
_
location
.
name
})
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
})
self
.
client
.
get
(
url
)
init_content
=
'<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">'
...
...
@@ -20,8 +20,8 @@ class CourseUpdateTest(CourseTestCase):
payload
=
{
'content'
:
content
,
'date'
:
'January 8, 2013'
}
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
resp
=
self
.
client
.
post
(
url
,
json
.
dumps
(
payload
),
"application/json"
)
...
...
@@ -31,8 +31,8 @@ class CourseUpdateTest(CourseTestCase):
self
.
assertHTMLEqual
(
payload
[
'content'
],
content
)
first_update_url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
payload
[
'id'
]})
content
+=
'<div>div <p>p<br/></p></div>'
payload
[
'content'
]
=
content
...
...
@@ -47,8 +47,8 @@ class CourseUpdateTest(CourseTestCase):
payload
=
{
'content'
:
content
,
'date'
:
'January 11, 2013'
}
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
resp
=
self
.
client
.
post
(
url
,
json
.
dumps
(
payload
),
"application/json"
)
...
...
@@ -58,8 +58,8 @@ class CourseUpdateTest(CourseTestCase):
self
.
assertHTMLEqual
(
content
,
payload
[
'content'
],
"self closing ol"
)
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
resp
=
self
.
client
.
get
(
url
)
payload
=
json
.
loads
(
resp
.
content
)
...
...
@@ -73,8 +73,8 @@ class CourseUpdateTest(CourseTestCase):
# now try to update a non-existent update
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
'9'
})
content
=
'blah blah'
payload
=
{
'content'
:
content
,
...
...
@@ -87,8 +87,8 @@ class CourseUpdateTest(CourseTestCase):
content
=
'<garbage tag No closing brace to force <span>error</span>'
payload
=
{
'content'
:
content
,
'date'
:
'January 11, 2013'
}
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
self
.
assertContains
(
...
...
@@ -99,8 +99,8 @@ class CourseUpdateTest(CourseTestCase):
content
=
"<p><br><br></p>"
payload
=
{
'content'
:
content
,
'date'
:
'January 11, 2013'
}
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
resp
=
self
.
client
.
post
(
url
,
json
.
dumps
(
payload
),
"application/json"
)
...
...
@@ -108,8 +108,8 @@ class CourseUpdateTest(CourseTestCase):
self
.
assertHTMLEqual
(
content
,
json
.
loads
(
resp
.
content
)[
'content'
])
# now try to delete a non-existent update
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
'19'
})
payload
=
{
'content'
:
content
,
'date'
:
'January 21, 2013'
}
...
...
@@ -119,8 +119,8 @@ class CourseUpdateTest(CourseTestCase):
content
=
'blah blah'
payload
=
{
'content'
:
content
,
'date'
:
'January 28, 2013'
}
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
resp
=
self
.
client
.
post
(
url
,
json
.
dumps
(
payload
),
"application/json"
)
payload
=
json
.
loads
(
resp
.
content
)
...
...
@@ -128,16 +128,16 @@ class CourseUpdateTest(CourseTestCase):
self
.
assertHTMLEqual
(
content
,
payload
[
'content'
],
"single iframe"
)
# first count the entries
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
''
})
resp
=
self
.
client
.
get
(
url
)
payload
=
json
.
loads
(
resp
.
content
)
before_delete
=
len
(
payload
)
url
=
reverse
(
'course_info_json'
,
kwargs
=
{
'org'
:
self
.
course
_
location
.
org
,
'course'
:
self
.
course
_
location
.
course
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'provided_id'
:
this_id
})
resp
=
self
.
client
.
delete
(
url
)
payload
=
json
.
loads
(
resp
.
content
)
...
...
cms/djangoapps/contentstore/tests/test_item.py
View file @
05cb13b1
...
...
@@ -22,7 +22,3 @@ class DeleteItem(CourseTestCase):
# Now delete it. There was a bug that the delete was failing (static tabs do not exist in draft modulestore).
resp
=
self
.
client
.
post
(
reverse
(
'delete_item'
),
resp
.
content
,
"application/json"
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
cms/djangoapps/contentstore/tests/test_textbooks.py
0 → 100644
View file @
05cb13b1
import
json
import
mock
from
unittest
import
TestCase
from
.utils
import
CourseTestCase
from
django.core.urlresolvers
import
reverse
from
contentstore.utils
import
get_modulestore
from
contentstore.views.course
import
(
validate_textbook_json
,
TextbookValidationError
)
class
TextbookTestCase
(
CourseTestCase
):
def
setUp
(
self
):
super
(
TextbookTestCase
,
self
)
.
setUp
()
self
.
url
=
reverse
(
'textbook_index'
,
kwargs
=
{
'org'
:
self
.
course
.
location
.
org
,
'course'
:
self
.
course
.
location
.
course
,
'name'
:
self
.
course
.
location
.
name
,
})
def
test_view_index
(
self
):
resp
=
self
.
client
.
get
(
self
.
url
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# we don't have resp.context right now,
# due to bugs in our testing harness :(
if
resp
.
context
:
self
.
assertEqual
(
resp
.
context
[
'course'
],
self
.
course
)
def
test_view_index_xhr
(
self
):
resp
=
self
.
client
.
get
(
self
.
url
,
HTTP_ACCEPT
=
"application/json"
,
HTTP_X_REQUESTED_WITH
=
'XMLHttpRequest'
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
obj
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
self
.
course
.
pdf_textbooks
,
obj
)
def
test_view_index_xhr_post
(
self
):
textbooks
=
[
{
"tab_title"
:
"Hi, mom!"
},
{
"tab_title"
:
"Textbook 2"
},
]
# import nose; nose.tools.set_trace()
resp
=
self
.
client
.
post
(
self
.
url
,
data
=
json
.
dumps
(
textbooks
),
content_type
=
"application/json"
,
HTTP_ACCEPT
=
"application/json"
,
HTTP_X_REQUESTED_WITH
=
'XMLHttpRequest'
)
self
.
assertEqual
(
resp
.
status_code
,
204
)
self
.
assertEqual
(
resp
.
content
,
""
)
# reload course
store
=
get_modulestore
(
self
.
course
.
location
)
course
=
store
.
get_item
(
self
.
course
.
location
)
self
.
assertEqual
(
course
.
pdf_textbooks
,
textbooks
)
class
TextbookValidationTestCase
(
TestCase
):
def
test_happy_path
(
self
):
textbooks
=
[
{
"tab_title"
:
"Hi, mom!"
,
"url"
:
"/mom.pdf"
},
{
"tab_title"
:
"Textbook 2"
,
"chapters"
:
[
{
"title"
:
"Chapter 1"
,
"url"
:
"/ch1.pdf"
},
{
"title"
:
"Chapter 2"
,
"url"
:
"/ch2.pdf"
}
]
}
]
result
=
validate_textbook_json
(
json
.
dumps
(
textbooks
))
self
.
assertEqual
(
textbooks
,
result
)
def
test_invalid_json
(
self
):
with
self
.
assertRaises
(
TextbookValidationError
):
validate_textbook_json
(
"[{'abc'}]"
)
def
test_wrong_json
(
self
):
with
self
.
assertRaises
(
TextbookValidationError
):
validate_textbook_json
(
'{"tab_title": "Hi, mom!"}'
)
def
test_no_tab_title
(
self
):
with
self
.
assertRaises
(
TextbookValidationError
):
validate_textbook_json
(
'[{"url": "/textbook.pdf"}'
)
cms/djangoapps/contentstore/tests/utils.py
View file @
05cb13b1
...
...
@@ -6,6 +6,10 @@ import json
from
student.models
import
Registration
from
django.contrib.auth.models
import
User
from
django.test.client
import
Client
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
def
parse_json
(
response
):
...
...
@@ -21,3 +25,37 @@ def user(email):
def
registration
(
email
):
"""look up registration object by email"""
return
Registration
.
objects
.
get
(
user__email
=
email
)
class
CourseTestCase
(
ModuleStoreTestCase
):
def
setUp
(
self
):
"""
These tests need a user in the DB so that the django Test Client
can log them in.
They inherit from the ModuleStoreTestCase class so that the mongodb collection
will be cleared out before each test case execution and deleted
afterwards.
"""
uname
=
'testuser'
email
=
'test+courses@edx.org'
password
=
'foo'
# Create the use so we can log them in.
self
.
user
=
User
.
objects
.
create_user
(
uname
,
email
,
password
)
# Note that we do not actually need to do anything
# for registration if we directly mark them active.
self
.
user
.
is_active
=
True
# Staff has access to view all courses
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
self
.
client
=
Client
()
self
.
client
.
login
(
username
=
uname
,
password
=
password
)
self
.
course
=
CourseFactory
.
create
(
template
=
'i4x://edx/templates/course/Empty'
,
org
=
'MITx'
,
number
=
'999'
,
display_name
=
'Robot Super Course'
,
)
cms/djangoapps/contentstore/views/course.py
View file @
05cb13b1
...
...
@@ -416,6 +416,23 @@ def course_advanced_updates(request, org, course, name):
return
HttpResponse
(
response_json
,
mimetype
=
"application/json"
)
class
TextbookValidationError
(
Exception
):
pass
def
validate_textbook_json
(
text
):
try
:
obj
=
json
.
loads
(
text
)
except
ValueError
:
raise
TextbookValidationError
(
"invalid JSON"
)
if
not
isinstance
(
obj
,
(
list
,
tuple
)):
raise
TextbookValidationError
(
"must be JSON list"
)
for
textbook
in
obj
:
if
not
textbook
.
get
(
"tab_title"
):
raise
TextbookValidationError
(
"every textbook must have a tab_title"
)
return
obj
@login_required
@ensure_csrf_cookie
def
textbook_index
(
request
,
org
,
course
,
name
):
...
...
@@ -433,18 +450,10 @@ def textbook_index(request, org, course, name):
return
HttpResponse
(
json
.
dumps
(
course_module
.
pdf_textbooks
),
content_type
=
"application/json"
)
elif
request
.
method
==
'POST'
:
try
:
obj
=
json
.
loads
(
request
.
raw_post_data
)
except
ValueError
:
msg
=
{
"error"
:
"invalid JSON"
}
return
HttpResponseBadRequest
(
json
.
dumps
(
msg
),
content_type
=
"application/json"
)
if
not
isinstance
(
obj
,
(
list
,
tuple
)):
msg
=
{
"error"
:
"must be JSON list"
}
return
HttpResponseBadRequest
(
json
.
dumps
(
msg
),
content_type
=
"application/json"
)
for
textbook
in
obj
:
if
not
textbook
.
get
(
"tab_title"
):
msg
=
{
"error"
:
"every textbook must have a tab_title"
}
course_module
.
pdf_textbooks
=
validate_textbook_json
(
request
.
body
)
except
TextbookValidationError
as
e
:
msg
=
{
"error"
:
e
.
message
}
return
HttpResponseBadRequest
(
json
.
dumps
(
msg
),
content_type
=
"application/json"
)
course_module
.
pdf_textbooks
=
obj
if
not
any
(
tab
[
'type'
]
==
'pdf_textbooks'
for
tab
in
course_module
.
tabs
):
course_module
.
tabs
.
append
({
"type"
:
"pdf_textbooks"
})
store
.
update_metadata
(
course_module
.
location
,
own_metadata
(
course_module
))
...
...
cms/manage.py
100644 → 100755
View file @
05cb13b1
File mode changed from 100644 to 100755
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment