Commit b406ba0f by Sarina Canelake

Acceptance tests for grades download

Refactor instructor dash acceptance tests.
Add additional acceptance tests for buttons on 'Data Download' tab.

LMS-58
parent c547ac72
......@@ -54,6 +54,11 @@ def register_by_course_id(course_id, username='robot', password='test', is_staff
@world.absorb
def enroll_user(user, course_id):
# Activate user
registration = world.RegistrationFactory(user=user)
registration.register(user)
registration.activate()
# Enroll them in the course
CourseEnrollment.enroll(user, course_id)
......
......@@ -5,7 +5,8 @@ Feature: LMS.Instructor Dash Bulk Email
I want to send email to staff and students in a course.
Scenario: Send bulk email
Given I am "<Role>" for a course
Given there is a course with a staff, instructor and student
And I am logged in to the course as "<Role>"
When I send email to "<Recipient>"
Then Email is sent to "<Recipient>"
......
......@@ -14,11 +14,11 @@ from django.conf import settings
from courseware.tests.factories import StaffFactory, InstructorFactory
@step(u'Given I am "([^"]*)" for a course')
def i_am_an_instructor(step, role): # pylint: disable=W0613
# Store the role
assert_in(role, ['instructor', 'staff'])
@step(u'Given there is a course with a staff, instructor and student')
def make_populated_course(step): # pylint: disable=unused-argument
## This is different than the function defined in common.py because it enrolls
## a staff, instructor, and student member regardless of what `role` is, then
## logs `role` in. This is to ensure we have 3 class participants to email.
# Clear existing courses to avoid conflicts
world.clear_courses()
......@@ -26,56 +26,37 @@ def i_am_an_instructor(step, role): # pylint: disable=W0613
# Create a new course
course = world.CourseFactory.create(
org='edx',
number='999',
display_name='Test Course'
number='888',
display_name='Bulk Email Test Course'
)
world.course_id = 'edx/999/Test_Course'
world.bulk_email_course_id = 'edx/888/Bulk_Email_Test_Course'
try:
# See if we've defined the instructor & staff user yet
world.instructor
world.bulk_email_instructor
except AttributeError:
# Make & register an instructor for the course
world.instructor = InstructorFactory(course=course.location)
world.enroll_user(world.instructor, world.course_id)
world.bulk_email_instructor = InstructorFactory(course=course.location)
world.enroll_user(world.bulk_email_instructor, world.bulk_email_course_id)
# Make & register a staff member
world.staff = StaffFactory(course=course.location)
world.enroll_user(world.staff, world.course_id)
world.bulk_email_staff = StaffFactory(course=course.location)
world.enroll_user(world.bulk_email_staff, world.bulk_email_course_id)
# Make & register a student
world.register_by_course_id(
'edx/999/Test_Course',
'edx/888/Bulk_Email_Test_Course',
username='student',
password='test',
is_staff=False
)
# Log in as the an instructor or staff for the course
my_email = None
if role == 'instructor':
my_email = world.instructor.email
world.log_in(
username=world.instructor.username,
password='test',
email=my_email,
name=world.instructor.profile.name
)
else:
my_email = world.staff.email
world.log_in(
username=world.staff.username,
password='test',
email=world.staff.email,
name=world.staff.profile.name
)
# Store the expected recipients
# given each "send to" option
staff_emails = [world.bulk_email_staff.email, world.bulk_email_instructor.email]
world.expected_addresses = {
'myself': [my_email],
'course staff': [world.staff.email, world.instructor.email],
'students, staff, and instructors': [world.staff.email, world.instructor.email, 'student@edx.org']
'course staff': staff_emails,
'students, staff, and instructors': staff_emails + ['student@edx.org']
}
......@@ -88,8 +69,35 @@ SEND_TO_OPTIONS = {
}
@step(u'I am logged in to the course as "([^"]*)"')
def log_into_the_course(step, role): # pylint: disable=unused-argument
# Store the role
assert_in(role, ['instructor', 'staff'])
# Log in as the an instructor or staff for the course
my_email = world.bulk_email_instructor.email
if role == 'instructor':
world.log_in(
username=world.bulk_email_instructor.username,
password='test',
email=my_email,
name=world.bulk_email_instructor.profile.name
)
else:
my_email = world.bulk_email_staff.email
world.log_in(
username=world.bulk_email_staff.username,
password='test',
email=my_email,
name=world.bulk_email_staff.profile.name
)
# Store the "myself" send to option
world.expected_addresses['myself'] = [my_email]
@step(u'I send email to "([^"]*)"')
def when_i_send_an_email(step, recipient):
def when_i_send_an_email(step, recipient): # pylint: disable=unused-argument
# Check that the recipient is valid
assert_in(
......@@ -107,8 +115,8 @@ def when_i_send_an_email(step, recipient):
call_command('loaddata', 'course_email_template.json')
# Go to the email section of the instructor dash
world.visit('/courses/edx/999/Test_Course')
world.css_click('a[href="/courses/edx/999/Test_Course/instructor"]')
world.visit('/courses/edx/888/Bulk_Email_Test_Course')
world.css_click('a[href="/courses/edx/888/Bulk_Email_Test_Course/instructor"]')
world.css_click('div.beta-button-wrapper>a')
world.css_click('a[data-section="send_email"]')
......@@ -138,7 +146,7 @@ UNSUBSCRIBE_MSG = 'To stop receiving email like this'
@step(u'Email is sent to "([^"]*)"')
def then_the_email_is_sent(step, recipient):
def then_the_email_is_sent(step, recipient): # pylint: disable=unused-argument
# Check that the recipient is valid
assert_in(
recipient, SEND_TO_OPTIONS,
......
"""
Define common steps for instructor dashboard acceptance tests.
"""
# pylint: disable=C0111
# pylint: disable=W0621
from __future__ import absolute_import
from lettuce import world, step
from nose.tools import assert_in # pylint: disable=E0611
from courseware.tests.factories import StaffFactory, InstructorFactory
@step(u'Given I am "([^"]*)" for a course')
def i_am_staff_or_instructor(step, role): # pylint: disable=unused-argument
## In summary: makes a test course, makes a new Staff or Instructor user
## (depending on `role`), and logs that user in to the course
# Store the role
assert_in(role, ['instructor', 'staff'])
# Clear existing courses to avoid conflicts
world.clear_courses()
# Create a new course
course = world.CourseFactory.create(
org='edx',
number='999',
display_name='Test Course'
)
world.course_id = 'edx/999/Test_Course'
world.role = 'instructor'
# Log in as the an instructor or staff for the course
if role == 'instructor':
# Make & register an instructor for the course
world.instructor = InstructorFactory(course=course.location)
world.enroll_user(world.instructor, world.course_id)
world.log_in(
username=world.instructor.username,
password='test',
email=world.instructor.email,
name=world.instructor.profile.name
)
else:
world.role = 'staff'
# Make & register a staff member
world.staff = StaffFactory(course=course.location)
world.enroll_user(world.staff, world.course_id)
world.log_in(
username=world.staff.username,
password='test',
email=world.staff.email,
name=world.staff.profile.name
)
def go_to_section(section_name):
# section name should be one of
# course_info, membership, student_admin, data_download, analytics, send_email
world.visit('/courses/edx/999/Test_Course')
world.css_click('a[href="/courses/edx/999/Test_Course/instructor"]')
world.css_click('div.beta-button-wrapper>a')
world.css_click('a[data-section="{0}"]'.format(section_name))
@step(u'I click "([^"]*)"')
def click_a_button(step, button): # pylint: disable=unused-argument
if button == "Generate Grade Report":
# Go to the data download section of the instructor dash
go_to_section("data_download")
# Click generate grade report button
world.css_click('input[name="calculate-grades-csv"]')
# Expect to see a message that grade report is being generated
expected_msg = "Your grade report is being generated! You can view the status of the generation task in the 'Pending Instructor Tasks' section."
world.wait_for_visible('#grade-request-response')
assert_in(
expected_msg, world.css_text('#grade-request-response'),
msg="Could not find grade report generation success message."
)
elif button == "Grading Configuration":
# Go to the data download section of the instructor dash
go_to_section("data_download")
world.css_click('input[name="dump-gradeconf"]')
elif button == "List enrolled students' profile information":
# Go to the data download section of the instructor dash
go_to_section("data_download")
world.css_click('input[name="list-profiles"]')
else:
raise ValueError("Unrecognized button option " + button)
@shard_2
Feature: LMS.Instructor Dash Data Download
As an instructor or course staff,
In order to manage my class
I want to view and download data information about my students.
### todos when more time can be spent on instructor dashboard
#Scenario: Download profile information as a CSV
#Scenario: Download student anonymized IDs as a CSV
Scenario: List enrolled students' profile information
Given I am "<Role>" for a course
When I click "List enrolled students' profile information"
Then I see a table of student profiles
Examples:
| Role |
| instructor |
| staff |
Scenario: View the grading configuration
Given I am "<Role>" for a course
When I click "Grading Configuration"
Then I see the grading configuration for the course
Examples:
| Role |
| instructor |
| staff |
Scenario: Generate & download a grade report
Given I am "<Role>" for a course
When I click "Generate Grade Report"
Then I see a csv file in the grade reports table
Examples:
| Role |
| instructor |
| staff |
"""
Define steps for instructor dashboard - data download tab
acceptance tests.
"""
# pylint: disable=C0111
# pylint: disable=W0621
from lettuce import world, step
from nose.tools import assert_in, assert_regexp_matches # pylint: disable=E0611
from terrain.steps import reload_the_page
@step(u'I see a table of student profiles')
def find_student_profile_table(step): # pylint: disable=unused-argument
# Find the grading configuration display
world.wait_for_visible('#data-student-profiles-table')
if world.role == 'instructor':
expected_data = [
world.instructor.username,
world.instructor.email,
world.instructor.profile.name,
world.instructor.profile.gender,
world.instructor.profile.goals
]
elif world.role == 'staff':
expected_data = [
world.staff.username,
world.staff.email,
world.staff.profile.name,
world.staff.profile.gender,
world.staff.profile.goals
]
for datum in expected_data:
assert_in(datum, world.css_text('#data-student-profiles-table'))
@step(u"I see the grading configuration for the course")
def find_grading_config(step): # pylint: disable=unused-argument
# Find the grading configuration display
world.wait_for_visible('#data-grade-config-text')
# expected config is the default grading configuration from common/lib/xmodule/xmodule/course_module.py
expected_config = u"""-----------------------------------------------------------------------------
Course grader:
<class 'xmodule.graders.WeightedSubsectionsGrader'>
Graded sections:
subgrader=<class 'xmodule.graders.AssignmentFormatGrader'>, type=Homework, category=Homework, weight=0.15
subgrader=<class 'xmodule.graders.AssignmentFormatGrader'>, type=Lab, category=Lab, weight=0.15
subgrader=<class 'xmodule.graders.AssignmentFormatGrader'>, type=Midterm Exam, category=Midterm Exam, weight=0.3
subgrader=<class 'xmodule.graders.AssignmentFormatGrader'>, type=Final Exam, category=Final Exam, weight=0.4
-----------------------------------------------------------------------------
Listing grading context for course edx/999/Test_Course
graded sections:
[]
all descriptors:
length=0"""
assert_in(expected_config, world.css_text('#data-grade-config-text'))
@step(u"I see a csv file in the grade reports table")
def find_grade_report_csv_link(step): # pylint: disable=unused-argument
# Need to reload the page to see the grades download table
reload_the_page(step)
world.wait_for_visible('#grade-downloads-table')
# Find table and assert a .csv file is present
expected_file_regexp = 'edx_999_Test_Course_grade_report_\d{4}-\d{2}-\d{2}-\d{4}\.csv'
assert_regexp_matches(
world.css_html('#grade-downloads-table'), expected_file_regexp,
msg="Expected grade report filename was not found."
)
......@@ -36,6 +36,10 @@ FEATURES['ENABLE_INSTRUCTOR_BETA_DASHBOARD'] = True
FEATURES['ENABLE_SHOPPING_CART'] = True
# Enable this feature for course staff grade downloads, to enable acceptance tests
FEATURES['ENABLE_S3_GRADE_DOWNLOADS'] = True
FEATURES['ALLOW_COURSE_STAFF_GRADE_DOWNLOADS'] = True
# Need wiki for courseware views to work. TODO (vshnayder): shouldn't need it.
WIKI_ENABLED = True
......
......@@ -4,19 +4,19 @@
<div class="data-download-container action-type-container">
<h2>${_("Data Download")}</h2>
<div class="request-response-error msg msg-error copy"></div>
<div class="request-response-error msg msg-error copy" id="data-request-response-error"></div>
<p>${_("The following button displays a list of all students enrolled in this course, along with profile information such as email address and username. The data can also be downloaded as a CSV file.")}</p>
<p><input type="button" name="list-profiles" value="${_("List enrolled students' profile information")}" data-endpoint="${ section_data['get_students_features_url'] }">
<input type="button" name="list-profiles" value="${_("Download profile information as a CSV")}" data-csv="true"></p>
<div class="data-display-table"></div>
<div class="data-display-table" id="data-student-profiles-table"></div>
<br>
<p>${_("Displays the grading configuration for the course. The grading configuration is the breakdown of graded subsections of the course (such as exams and problem sets), and can be changed on the 'Grading' page (under 'Settings') in Studio.")}</p>
<p><input type="button" name="dump-gradeconf" value="${_("Grading Configuration")}" data-endpoint="${ section_data['get_grading_config_url'] }"></p>
<div class="data-display-text"></div>
<div class="data-display-text" id="data-grade-config-text"></div>
<br>
<p>${_("Download a CSV of anonymized student IDs by clicking this button.")}</p>
......@@ -33,8 +33,8 @@
<p>${_("The report is generated in the background, meaning it is OK to navigate away from this page while your report is generating. Generated reports appear in a table below and can be downloaded.")}</p>
<div class="request-response msg msg-confirm copy"></div>
<div class="request-response-error msg msg-warning copy"></div>
<div class="request-response msg msg-confirm copy" id="grade-request-response"></div>
<div class="request-response-error msg msg-warning copy" id="grade-request-response-error"></div>
<br>
<p><input type="button" name="calculate-grades-csv" value="${_("Generate Grade Report")}" data-endpoint="${ section_data['calculate_grades_csv_url'] }"/></p>
......@@ -42,7 +42,7 @@
<p><b>${_("Reports Available for Download")}</b></p>
<p>${_("File links are generated on demand and expire within 5 minutes due to the sensitive nature of student grade information. Please note that the report filename contains a timestamp that represents when your file was generated; this timestamp is UTC, not your local timezone.")}</p><br>
<div class="grade-downloads-table" data-endpoint="${ section_data['list_grade_downloads_url'] }" ></div>
<div class="grade-downloads-table" id="grade-downloads-table" data-endpoint="${ section_data['list_grade_downloads_url'] }" ></div>
</div>
%endif
......
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