Refactored bok-choy directory structure

Added fixtures for course and xblock creation
Added bok-choy Studio tests
Added bok-choy tests for ora self- and ai- assessment
Refactored auto-auth; added staff and course-enrollment options
Removed extra javascript properties from page objects
This commit is contained in:
Will Daly
2014-01-03 13:38:15 -08:00
parent f3f0e8a514
commit 4afd5ea49f
57 changed files with 1524 additions and 450 deletions

View File

@@ -0,0 +1,10 @@
import logging
# Silence noisy loggers
LOG_OVERRIDES = [
('requests.packages.urllib3.connectionpool', logging.ERROR),
('django.db.backends', logging.ERROR)
]
for log_name, log_level in LOG_OVERRIDES:
logging.getLogger(log_name).setLevel(log_level)

View File

@@ -0,0 +1,30 @@
<combinedopenended max_score="2" markdown="null" max_attempts="1000">
<rubric>
<rubric>
<category>
<description>Writing Applications</description>
<option> The essay loses focus, has little information or supporting details, and the organization makes it difficult to follow.</option>
<option> The essay presents a mostly unified theme, includes sufficient information to convey the theme, and is generally organized well.</option>
</category>
<category>
<description> Language Conventions </description>
<option> The essay demonstrates a reasonable command of proper spelling and grammar. </option>
<option> The essay demonstrates superior command of proper spelling and grammar.</option>
</category>
</rubric>
</rubric>
<prompt>
<h4>Censorship in the Libraries</h4>
<p>"All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us." --Katherine Paterson, Author</p>
<p>Write a persuasive essay to a newspaper reflecting your vies on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading.</p>
</prompt>
<task>
<openended>
<openendedparam>
<initial_display>Enter essay here.</initial_display>
<answer_display>This is the answer.</answer_display>
<grader_payload>{"grader_settings" : "ml_grading.conf", "problem_id" : "6.002x/Welcome/OETest"}</grader_payload>
</openendedparam>
</openended>
</task>
</combinedopenended>

View File

@@ -0,0 +1,24 @@
<combinedopenended max_score="1" accept_file_upload="False" markdown="null" max_attempts="1000" skip_spelling_checks="False" version="1">
<rubric>
<rubric>
<category>
<description>Writing Applications</description>
<option> The essay loses focus, has little information or supporting details, and the organization makes it difficult to follow.</option>
<option> The essay presents a mostly unified theme, includes sufficient information to convey the theme, and is generally organized well.</option>
</category>
<category>
<description> Language Conventions </description>
<option> The essay demonstrates a reasonable command of proper spelling and grammar. </option>
<option> The essay demonstrates superior command of proper spelling and grammar.</option>
</category>
</rubric>
</rubric>
<prompt>
<h4>Censorship in the Libraries</h4>
<p>"All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us." --Katherine Paterson, Author</p>
<p>Write a persuasive essay to a newspaper reflecting your views on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading.</p>
</prompt>
<task>
<selfassessment/>
</task>
</combinedopenended>

View File

@@ -0,0 +1,14 @@
"""
Test helper functions.
"""
from path import path
def load_data_str(rel_path):
"""
Load a file from the "data" directory as a string.
`rel_path` is the path relative to the data directory.
"""
full_path = path(__file__).abspath().dirname() / "data" / rel_path #pylint: disable=E1120
with open(full_path) as data_file:
return data_file.read()

View File

@@ -0,0 +1,132 @@
"""
Tests for ORA (Open Response Assessment) through the LMS UI.
"""
from bok_choy.web_app_test import WebAppTest
from ..edxapp_pages.studio.auto_auth import AutoAuthPage
from ..edxapp_pages.lms.course_info import CourseInfoPage
from ..edxapp_pages.lms.tab_nav import TabNavPage
from ..edxapp_pages.lms.course_nav import CourseNavPage
from ..edxapp_pages.lms.open_response import OpenResponsePage
from ..fixtures.course import XBlockFixtureDesc, CourseFixture
from .helpers import load_data_str
class OpenResponseTest(WebAppTest):
"""
Tests that interact with ORA (Open Response Assessment) through the LMS UI.
"""
def setUp(self):
"""
Always start in the subsection with open response problems.
"""
# Create a unique course ID
self.course_info = {
'org': 'test_org',
'number': self.unique_id,
'run': 'test_run',
'display_name': 'Test Course' + self.unique_id
}
# Ensure fixtures are installed
super(OpenResponseTest, self).setUp()
# Log in and navigate to the essay problems
course_id = '{org}/{number}/{run}'.format(**self.course_info)
self.ui.visit('studio.auto_auth', course_id=course_id)
self.ui.visit('lms.course_info', course_id=course_id)
self.ui['lms.tab_nav'].go_to_tab('Courseware')
self.ui['lms.course_nav'].go_to_section(
'Example Week 2: Get Interactive', 'Homework - Essays'
)
@property
def page_object_classes(self):
return [AutoAuthPage, CourseInfoPage, TabNavPage, CourseNavPage, OpenResponsePage]
@property
def fixtures(self):
"""
Create a test course with open response problems.
"""
course_fix = CourseFixture(
self.course_info['org'], self.course_info['number'],
self.course_info['run'], self.course_info['display_name']
)
course_fix.add_children(
XBlockFixtureDesc('chapter', 'Test Section').add_children(
XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
XBlockFixtureDesc('combinedopenended', 'Self-Assessed', data=load_data_str('ora_self_problem.xml')),
XBlockFixtureDesc('combinedopenended', 'AI-Assessed', data=load_data_str('ora_ai_problem.xml'))
)
)
)
return [course_fix]
def test_self_assessment(self):
"""
Test that the user can self-assess an essay.
"""
# Navigate to the self-assessment problem and submit an essay
self.ui['lms.course_nav'].go_to_sequential('Self-Assessed')
self._submit_essay('self', 'Censorship in the Libraries')
# Check the rubric categories
self.assertEqual(
self.ui['lms.open_response'].rubric_categories,
["Writing Applications", "Language Conventions"]
)
# Fill in the self-assessment rubric
self.ui['lms.open_response'].submit_self_assessment([0, 1])
# Expect that we get feedback
self.assertEqual(
self.ui['lms.open_response'].rubric_feedback,
['incorrect', 'correct']
)
def test_ai_assessment(self):
"""
Test that a user can submit an essay and receive AI feedback.
"""
# Navigate to the AI-assessment problem and submit an essay
self.ui['lms.course_nav'].go_to_sequential('AI-Assessed')
self._submit_essay('ai', 'Censorship in the Libraries')
# Expect UI feedback that the response was submitted
self.assertEqual(
self.ui['lms.open_response'].grader_status,
"Your response has been submitted. Please check back later for your grade."
)
def _submit_essay(self, expected_assessment_type, expected_prompt):
"""
Submit an essay and verify that the problem uses
the `expected_assessment_type` ("self", "ai", or "peer") and
shows the `expected_prompt` (a string).
"""
# Check the assessment type and prompt
self.assertEqual(self.ui['lms.open_response'].assessment_type, expected_assessment_type)
self.assertIn(expected_prompt, self.ui['lms.open_response'].prompt)
# Enter a response
essay = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vehicula."
self.ui['lms.open_response'].set_response(essay)
# Save the response and expect some UI feedback
self.ui['lms.open_response'].save_response()
self.assertEqual(
self.ui['lms.open_response'].alert_message,
"Answer saved, but not yet submitted."
)
# Submit the response
self.ui['lms.open_response'].submit_response()

View File

@@ -0,0 +1,118 @@
"""
Acceptance tests for Studio.
"""
from bok_choy.web_app_test import WebAppTest
from ..edxapp_pages.studio.asset_index import AssetIndexPage
from ..edxapp_pages.studio.auto_auth import AutoAuthPage
from ..edxapp_pages.studio.checklists import ChecklistsPage
from ..edxapp_pages.studio.course_import import ImportPage
from ..edxapp_pages.studio.course_info import CourseUpdatesPage
from ..edxapp_pages.studio.edit_tabs import StaticPagesPage
from ..edxapp_pages.studio.export import ExportPage
from ..edxapp_pages.studio.howitworks import HowitworksPage
from ..edxapp_pages.studio.index import DashboardPage
from ..edxapp_pages.studio.login import LoginPage
from ..edxapp_pages.studio.manage_users import CourseTeamPage
from ..edxapp_pages.studio.overview import CourseOutlinePage
from ..edxapp_pages.studio.settings import SettingsPage
from ..edxapp_pages.studio.settings_advanced import AdvancedSettingsPage
from ..edxapp_pages.studio.settings_graders import GradingPage
from ..edxapp_pages.studio.signup import SignupPage
from ..edxapp_pages.studio.textbooks import TextbooksPage
from ..fixtures.course import CourseFixture
class LoggedOutTest(WebAppTest):
"""
Smoke test for pages in Studio that are visible when logged out.
"""
@property
def page_object_classes(self):
return [LoginPage, HowitworksPage, SignupPage]
def test_page_existence(self):
"""
Make sure that all the pages are accessible.
Rather than fire up the browser just to check each url,
do them all sequentially in this testcase.
"""
for page in ['login', 'howitworks', 'signup']:
self.ui.visit('studio.{0}'.format(page))
class LoggedInPagesTest(WebAppTest):
"""
Tests that verify the pages in Studio that you can get to when logged
in and do not have a course yet.
"""
@property
def page_object_classes(self):
return [AutoAuthPage, DashboardPage]
def test_dashboard_no_courses(self):
"""
Make sure that you can get to the dashboard page without a course.
"""
self.ui.visit('studio.auto_auth', staff=True)
self.ui.visit('studio.dashboard')
class CoursePagesTest(WebAppTest):
"""
Tests that verify the pages in Studio that you can get to when logged
in and have a course.
"""
def setUp(self):
"""
Create a unique identifier for the course used in this test.
"""
# Define a unique course identifier
self.course_info = {
'org': 'test_org',
'number': '101',
'run': 'test_' + self.unique_id,
'display_name': 'Test Course ' + self.unique_id
}
# Ensure that the superclass sets up
super(CoursePagesTest, self).setUp()
@property
def page_object_classes(self):
return [
AutoAuthPage, AssetIndexPage, ChecklistsPage, ImportPage, CourseUpdatesPage,
StaticPagesPage, ExportPage, CourseTeamPage, CourseOutlinePage,
SettingsPage, AdvancedSettingsPage, GradingPage, TextbooksPage
]
@property
def fixtures(self):
super_fixtures = super(CoursePagesTest, self).fixtures
course_fix = CourseFixture(
self.course_info['org'],
self.course_info['number'],
self.course_info['run'],
self.course_info['display_name']
)
return set(super_fixtures + [course_fix])
def test_page_existence(self):
"""
Make sure that all these pages are accessible once you have a course.
Rather than fire up the browser just to check each url,
do them all sequentially in this testcase.
"""
pages = [
'uploads', 'checklists', 'import', 'updates', 'tabs', 'export',
'team', 'outline', 'settings', 'advanced', 'grading', 'textbooks'
]
# Log in
self.ui.visit('studio.auto_auth', staff=True)
course_id = '{org}.{number}.{run}'.format(**self.course_info)
for page in pages:
self.ui.visit('studio.{0}'.format(page), course_id=course_id)