Commit cda560ca by Andy Armstrong

Implement new toolbar for staff area

TNL-3466
parent f53a54d7
......@@ -18,3 +18,4 @@ Omar Al-Ithawi <oithawi@qrf.org>
Ahsan Ulhaq <ahsan@edx.org>
Ben Patterson <bpatterson@edx.org>
Eric Fischer <efischer@edx.org>
Andy Armstrong <andya@edx.org>
......@@ -83,7 +83,7 @@ files:
make javascript
# Combine/minify CSS (from Sass)
./scripts/sass.sh
make sass
Make sure you commit the combined/minified files to the git repository!
......@@ -107,13 +107,13 @@ To run just the JavaScript tests:
.. code:: bash
./scripts/test-js.sh
make test-js
To run the JavaScript tests in Chrome so you can use the debugger:
.. code:: bash
./scripts/js-debugger.sh
make test-js-debug
There are also acceptance and accessibility tests that run can be run against a sandbox. For more information, about how to run these from your machine, check out `test/acceptance/README.rst <https://github.com/edx/edx-ora2/blob/master/test/acceptance/README.rst/>`__.
......
......@@ -39,8 +39,8 @@
{% endfor %}
</ol>
{% if show_staff_debug_info %}
<div id="openassessment__staff-info"></div>
{% if show_staff_area %}
<div id="openassessment__staff-area"></div>
{% endif %}
</div>
</div>
......
......@@ -29,7 +29,7 @@ from openassessment.xblock.self_assessment_mixin import SelfAssessmentMixin
from openassessment.xblock.submission_mixin import SubmissionMixin
from openassessment.xblock.studio_mixin import StudioMixin
from openassessment.xblock.xml import parse_from_xml, serialize_content_to_xml
from openassessment.xblock.staff_info_mixin import StaffInfoMixin
from openassessment.xblock.staff_area_mixin import StaffAreaMixin
from openassessment.xblock.workflow_mixin import WorkflowMixin
from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.student_training_mixin import StudentTrainingMixin
......@@ -103,7 +103,7 @@ class OpenAssessmentBlock(
StudioMixin,
GradeMixin,
LeaderboardMixin,
StaffInfoMixin,
StaffAreaMixin,
WorkflowMixin,
StudentTrainingMixin,
LmsCompatibilityMixin,
......@@ -301,7 +301,7 @@ class OpenAssessmentBlock(
"title": self.title,
"prompts": self.prompts,
"rubric_assessments": ui_models,
"show_staff_debug_info": self.is_course_staff and not self.in_studio_preview,
"show_staff_area": self.is_course_staff and not self.in_studio_preview,
}
template = get_template("openassessmentblock/oa_base.html")
context = Context(context_dict)
......
"""
The Staff Info View mixin renders all the staff-specific information used to
The Staff Area View mixin renders all the staff-specific information used to
determine the flow of the problem.
"""
import copy
......@@ -75,9 +75,8 @@ def require_course_staff(error_key, with_json_handler=False):
@wraps(func)
def _wrapped(xblock, *args, **kwargs): # pylint: disable=C0111
permission_errors = {
"STAFF_INFO": xblock._(u"You do not have permission to access staff information"),
"STAFF_AREA": xblock._(u"You do not have permission to access the staff area"),
"STUDENT_INFO": xblock._(u"You do not have permission to access learner information."),
}
if not xblock.is_course_staff and with_json_handler:
......@@ -90,14 +89,14 @@ def require_course_staff(error_key, with_json_handler=False):
return _decorator
class StaffInfoMixin(object):
class StaffAreaMixin(object):
"""
Display debug information to course and global staff.
"""
@XBlock.handler
@require_course_staff("STAFF_INFO")
def render_staff_info(self, data, suffix=''): # pylint: disable=W0613
@require_course_staff("STAFF_AREA")
def render_staff_area(self, data, suffix=''): # pylint: disable=W0613
"""
Template context dictionary for course staff debug panel.
......@@ -113,7 +112,7 @@ class StaffInfoMixin(object):
Gets the path and context for the staff section of the ORA XBlock.
"""
context = {}
path = 'openassessmentblock/staff_debug/staff_debug.html'
path = 'openassessmentblock/staff_area/staff_area.html'
student_item = self.get_student_item_dict()
......@@ -145,14 +144,12 @@ class StaffInfoMixin(object):
student_item['item_id']
)
# Include release/due dates for each step in the problem
context['step_dates'] = list()
# Include Latex setting
context['allow_latex'] = self.allow_latex
steps = ['submission'] + self.assessment_steps
for step in steps:
# Include release/due dates for each step in the problem
context['step_dates'] = list()
for step in ['submission'] + self.assessment_steps:
if step == 'example-based-assessment':
continue
......@@ -300,7 +297,7 @@ class StaffInfoMixin(object):
for criterion in context["rubric_criteria"]:
criterion["total_value"] = max_scores[criterion["name"]]
path = 'openassessmentblock/staff_debug/student_info.html'
path = 'openassessmentblock/staff_area/student_info.html'
return path, context
@XBlock.json_handler
......@@ -376,12 +373,15 @@ class StaffInfoMixin(object):
cancelled_by_id=student_item_dict['student_id'],
assessment_requirements=assessment_requirements
)
return {"success": True, 'msg': self._(
u"The learner submission has been removed from peer assessment. "
u"The learner receives a grade of zero unless you reset "
u"the learner's attempts for the problem to allow them to "
u"resubmit a response."
)}
return {
"success": True,
'msg': self._(
u"The learner submission has been removed from peer assessment. "
u"The learner receives a grade of zero unless you reset "
u"the learner's attempts for the problem to allow them to "
u"resubmit a response."
)
}
except (
AssessmentWorkflowError,
AssessmentWorkflowInternalError
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,7 +2,7 @@
{
"template": "openassessmentblock/oa_base.html",
"context": {
"show_staff_debug_info": false,
"show_staff_area": false,
"title": "Test title",
"question": "Test prompt",
"rubric_criteria": [],
......@@ -44,7 +44,7 @@
{
"template": "openassessmentblock/oa_base.html",
"context": {
"show_staff_debug_info": true,
"show_staff_area": true,
"title": "Test title",
"question": "Test prompt",
"rubric_criteria": [],
......@@ -651,7 +651,7 @@
"output": "oa_edit_student_training.html"
},
{
"template": "openassessmentblock/staff_debug/staff_debug.html",
"template": "openassessmentblock/staff_area/staff_area.html",
"context": {
"status_counts": {
"self": 1,
......@@ -679,10 +679,10 @@
}
]
},
"output": "oa_staff_info.html"
"output": "oa_staff_area.html"
},
{
"template": "openassessmentblock/staff_debug/student_info.html",
"template": "openassessmentblock/staff_area/student_info.html",
"context": {
"submission": {
"image_url": "/test-url",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -20,20 +20,20 @@ describe("OpenAssessment.ResponseView", function() {
function(defer) { defer.rejectWith(this, ["ERROR"]); }
).promise();
this.save = function(submission) {
this.save = function() {
return successPromise;
};
this.submit = function(submission) {
this.submit = function() {
return successPromise;
};
this.render = function(step) {
this.render = function() {
return successPromise;
};
this.uploadUrlError = false;
this.getUploadUrl = function(contentType) {
this.getUploadUrl = function() {
return this.uploadUrlError ? errorPromise : successPromiseWithUrl;
};
......@@ -66,9 +66,9 @@ describe("OpenAssessment.ResponseView", function() {
this.loadAssessmentModules = function() {};
this.peerView = { load: function() {} };
this.gradeView = { load: function() {} };
this.showLoadError = function(msg) {};
this.toggleActionError = function(msg, step) {};
this.setUpCollapseExpand = function(sel) {};
this.showLoadError = function() {};
this.toggleActionError = function() {};
this.setUpCollapseExpand = function() {};
};
// Stubs
......@@ -100,7 +100,7 @@ describe("OpenAssessment.ResponseView", function() {
// Create stub objects
server = new StubServer();
server.renderLatex = jasmine.createSpy('renderLatex')
server.renderLatex = jasmine.createSpy('renderLatex');
fileUploader = new StubFileUploader();
baseView = new StubBaseView();
......@@ -228,7 +228,7 @@ describe("OpenAssessment.ResponseView", function() {
// Prevent the server's response from resolving,
// so we can see what happens before view gets re-rendered.
spyOn(server, 'submit').and.callFake(function() {
return $.Deferred(function(defer) {}).promise();
return $.Deferred(function() {}).promise();
});
view.response(['Test response 1', 'Test response 2']);
......
......@@ -22,8 +22,8 @@ OpenAssessment.BaseView = function(runtime, element, server) {
this.gradeView = new OpenAssessment.GradeView(this.element, this.server, this);
this.leaderboardView = new OpenAssessment.LeaderboardView(this.element, this.server, this);
this.messageView = new OpenAssessment.MessageView(this.element, this.server, this);
// Staff only information about student progress.
this.staffInfoView = new OpenAssessment.StaffInfoView(this.element, this.server, this);
// Staff-only area with information and tools for managing student submissions
this.staffAreaView = new OpenAssessment.StaffAreaView(this.element, this.server, this);
};
......@@ -62,7 +62,7 @@ OpenAssessment.BaseView.prototype = {
load: function() {
this.responseView.load();
this.loadAssessmentModules();
this.staffInfoView.load();
this.staffAreaView.load();
},
/**
......
......@@ -26,6 +26,83 @@
// --------------------
// Developer styles for Staff Section
// --------------------
// edX constants
$lighter-base-font-color: rgb(100,100,100);
$link-hover: $edx-blue-l1 !default; // from our Pattern Library http://ux.edx.org/elements/colors/
.wrapper--openassessment .wrapper--staff-area {
width: 100%;
overflow: auto;
margin-top: $baseline-v;
padding-top: 20px;
.wrapper--staff-toolbar {
position: relative;
text-align: right;
margin: 0 0 8px;
padding: 10px;
.ui-staff__button {
margin-left: ($baseline-v/2);
padding: ($baseline-v/4) ($baseline-v/2);
border-radius: ($baseline-v/4);
text-transform: uppercase;
color: $lighter-base-font-color;
background-color: $shadow-l2;
// Remove button styling
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 12px;
-webkit-appearance: none;
background-image: none;
text-shadow: none;
box-shadow: none;
border: none;
border-image: none;
&.is--active {
color: white;
background-color: $edx-pink;
&:hover {
background-color: $edx-pink-d1;
}
}
&:hover {
color: white;
background-color: $link-hover;
}
}
}
.wrapper--ui-staff {
margin-top: 0;
.ui-staff_close_button {
margin: 0;
padding: 0;
border: 0;
float: right;
background-color: $staff-bg;
// Remove button styling
font-size: 12px;
-webkit-appearance: none;
background-image: none;
text-shadow: none;
box-shadow: none;
border: none;
border-image: none;
&:hover {
color: $white;
}
}
}
}
.staff-info__status {
.action--submit {
@extend %btn--secondary;
......
{
"name": "edx-ora2",
"version": "0.0.1",
"version": "0.2.0",
"repository": "https://github.com/edx/edx-ora2.git",
"devDependencies": {
"karma": "^0.12.16",
......
......@@ -4,4 +4,3 @@ cd `dirname $BASH_SOURCE` && cd ..
echo "Starting JavaScript tests in a browser..."
./node_modules/karma/bin/karma start --single-run=false --browsers Chrome --reporters=html --autoWatch
......@@ -42,7 +42,7 @@ def load_requirements(*requirements_paths):
setup(
name='ora2',
version='0.0.1',
version='0.2.0',
author='edX',
url='http://github.com/edx/edx-ora2',
description='edx-ora2',
......
......@@ -3,18 +3,20 @@ UI-level acceptance tests for OpenAssessment accessibility.
"""
import os
import unittest
from tests import OpenAssessmentTest
from tests import OpenAssessmentTest, StaffAreaPage
class OpenAssessmentAxsTest(OpenAssessmentTest):
class OpenAssessmentA11yTest(OpenAssessmentTest):
"""
UI-level acceptance tests for Open Assessment accessibility.
"""
def _check_axs(self):
def setUp(self, problem_type, staff=False):
super(OpenAssessmentA11yTest, self).setUp(problem_type, staff=staff)
self.auto_auth_page.visit()
self.submission_page.visit()
self.submission_page.a11y_audit.config.set_rules({
def _check_a11y(self, page):
page.a11y_audit.config.set_rules({
"ignore": [
"aria-valid-attr", # TODO: AC-199
"color-contrast", # TODO: AC-198
......@@ -22,42 +24,70 @@ class OpenAssessmentAxsTest(OpenAssessmentTest):
"link-name", # TODO: AC-196
]
})
report = self.submission_page.a11y_audit.check_for_accessibility_errors()
page.a11y_audit.check_for_accessibility_errors()
class SelfAssessmentAxsTest(OpenAssessmentAxsTest):
class SelfAssessmentA11yTest(OpenAssessmentA11yTest):
"""
Test the accessibility of the self-assessment flow.
"""
def setUp(self):
super(SelfAssessmentAxsTest, self).setUp('self_only')
super(SelfAssessmentA11yTest, self).setUp('self_only')
def test_self_assessment_axs(self):
self._check_axs()
def test_self_assessment_a11y(self):
self.submission_page.visit()
self._check_a11y(self.submission_page)
class PeerAssessmentAxsTest(OpenAssessmentAxsTest):
class PeerAssessmentA11yTest(OpenAssessmentA11yTest):
"""
Test the accessibility of the peer-assessment flow.
"""
def setUp(self):
super(PeerAssessmentAxsTest, self).setUp('peer_only')
super(PeerAssessmentA11yTest, self).setUp('peer_only')
def test_peer_assessment_axs(self):
self._check_axs()
def test_peer_assessment_a11y(self):
self.submission_page.visit()
self._check_a11y(self.submission_page)
class StudentTrainingAxsTest(OpenAssessmentAxsTest):
class StudentTrainingA11yTest(OpenAssessmentA11yTest):
"""
Test the accessibility of student training (the "learning to assess" step).
"""
def setUp(self):
super(StudentTrainingAxsTest, self).setUp('student_training')
super(StudentTrainingA11yTest, self).setUp('student_training')
def test_student_training_a11y(self):
self.submission_page.visit()
self._check_a11y(self.submission_page)
def test_student_training_axs(self):
self._check_axs()
class StaffAreaA11yTest(OpenAssessmentA11yTest):
"""
Test the accessibility of the staff area.
"""
def setUp(self):
super(StaffAreaA11yTest, self).setUp('peer_only', staff=True)
self.staff_area_page = StaffAreaPage(self.browser, self.problem_loc)
def test_staff_tools_panel_a11y(self):
"""
Check the accessibility of the "Staff Tools" panel
"""
self.staff_area_page.visit()
self.staff_area_page.click_staff_toolbar_button("staff-tools")
self._check_a11y(self.staff_area_page)
def test_staff_info_panel_a11y(self):
"""
Check the accessibility of the "Staff Info" panel
"""
self.staff_area_page.visit()
self.staff_area_page.click_staff_toolbar_button("staff-info")
self._check_a11y(self.staff_area_page)
if __name__ == "__main__":
......
......@@ -296,3 +296,43 @@ class GradePage(OpenAssessmentPage):
"""
score_candidates = [int(x) for x in self.q(css=".grade__value__earned").text]
return score_candidates[0] if len(score_candidates) > 0 else None
class StaffAreaPage(OpenAssessmentPage):
"""
Page object representing the "submission" step in an ORA problem.
"""
def is_browser_on_page(self):
return self.q(css="#openassessment__staff-area").is_present()
@property
def selected_button_names(self):
"""
Returns the names of the selected toolbar buttons.
"""
buttons = self.q(css=".ui-staff__button")
return [button.text for button in buttons if u'is--active' in button.get_attribute('class')]
@property
def visible_staff_panels(self):
"""
Returns the ids of the visible staff panels
"""
panels = self.q(css=".wrapper--ui-staff")
return [panel.get_attribute('id') for panel in panels if u'is--hidden' not in panel.get_attribute('class')]
def click_staff_toolbar_button(self, button_name):
"""
Presses the button to show the panel with the specified name.
:return:
"""
buttons = self.q(css=".button-{button_name}".format(button_name=button_name))
buttons.first.click()
def click_staff_panel_close_button(self, panel_name):
"""
Presses the close button on the staff panel with the specified name.
:return:
"""
self.q(css=".wrapper--{panel_name} .ui-staff_close_button".format(panel_name=panel_name)).click()
"""
UI-level acceptance tests for OpenAssessment.
"""
import ddt
import os
import unittest
import time
from functools import wraps
from nose.plugins.attrib import attr
from bok_choy.web_app_test import WebAppTest
from bok_choy.promise import BrokenPromise
from auto_auth import AutoAuthPage
from pages import (
SubmissionPage, AssessmentPage, GradePage
SubmissionPage, AssessmentPage, GradePage, StaffAreaPage
)
......@@ -65,24 +67,25 @@ class OpenAssessmentTest(WebAppTest):
OPTIONS_SELECTED = [1, 2]
EXPECTED_SCORE = 6
def setUp(self, problem_type):
def setUp(self, problem_type, staff=False):
"""
Configure page objects to test Open Assessment.
Args:
problem_type (str): The type of problem being tested,
used to choose which part of the course to load.
staff (bool): If True, runs the test with a staff user (defaults to False).
"""
super(OpenAssessmentTest, self).setUp()
problem_loc = self.PROBLEM_LOCATIONS[problem_type]
self.auto_auth_page = AutoAuthPage(self.browser, course_id=self.TEST_COURSE_ID)
self.submission_page = SubmissionPage(self.browser, problem_loc)
self.self_asmnt_page = AssessmentPage('self-assessment', self.browser, problem_loc)
self.peer_asmnt_page = AssessmentPage('peer-assessment', self.browser, problem_loc)
self.student_training_page = AssessmentPage('student-training', self.browser, problem_loc)
self.grade_page = GradePage(self.browser, problem_loc)
self.problem_loc = self.PROBLEM_LOCATIONS[problem_type]
self.auto_auth_page = AutoAuthPage(self.browser, course_id=self.TEST_COURSE_ID, staff=staff)
self.submission_page = SubmissionPage(self.browser, self.problem_loc)
self.self_asmnt_page = AssessmentPage('self-assessment', self.browser, self.problem_loc)
self.peer_asmnt_page = AssessmentPage('peer-assessment', self.browser, self.problem_loc)
self.student_training_page = AssessmentPage('student-training', self.browser, self.problem_loc)
self.grade_page = GradePage(self.browser, self.problem_loc)
class SelfAssessmentTest(OpenAssessmentTest):
......@@ -206,6 +209,79 @@ class StudentTrainingTest(OpenAssessmentTest):
self.fail("Student training was not marked complete.")
class StaffAreaTest(OpenAssessmentTest):
"""
Test the staff area.
"""
def setUp(self):
super(StaffAreaTest, self).setUp('peer_only', staff=True)
self.staff_area_page = StaffAreaPage(self.browser, self.problem_loc)
@retry()
@attr('acceptance')
def test_staff_area_buttons(self):
"""
Scenario: the staff area buttons should behave correctly
Given I am viewing the staff area of an ORA problem
Then none of the buttons should be active
When I click the "Staff Tools" button
Then only the "Staff Tools" button should be active
When I click the "Staff Info" button
Then only the "Staff Info" button should be active
When I click the "Staff Info" button again
Then none of the buttons should be active
"""
self.auto_auth_page.visit()
self.staff_area_page.visit()
self.assertEqual(self.staff_area_page.selected_button_names, [])
self.staff_area_page.click_staff_toolbar_button("staff-tools")
self.assertEqual(self.staff_area_page.selected_button_names, ["STAFF TOOLS"])
self.staff_area_page.click_staff_toolbar_button("staff-info")
self.assertEqual(self.staff_area_page.selected_button_names, ["STAFF INFO"])
self.staff_area_page.click_staff_toolbar_button("staff-info")
self.assertEqual(self.staff_area_page.selected_button_names, [])
@retry()
@attr('acceptance')
@ddt.data(
("staff-tools", "STAFF TOOLS"),
("staff-info", "STAFF INFO"),
)
@ddt.unpack
def test_staff_tools_panel(self, button_name, button_label):
"""
Scenario: the staff tools panel should be shown correctly
Given I am viewing the staff area of an ORA problem
Then none of the panels should be shown
When I click the staff button
Then only the related panel should be shown
When I click the close button in the panel
Then none of the panels should be shown
"""
self.auto_auth_page.visit()
self.staff_area_page.visit()
# Verify that there is no selected panel initially
self.assertEqual(self.staff_area_page.selected_button_names, [])
self.assertEqual(self.staff_area_page.visible_staff_panels, [])
# Click on the button and verify that the panel has opened
self.staff_area_page.click_staff_toolbar_button(button_name)
self.assertEqual(self.staff_area_page.selected_button_names, [button_label])
self.assertEqual(
self.staff_area_page.visible_staff_panels,
[u'openassessment__{button_name}'.format(button_name=button_name)]
)
# Click 'Close' and verify that the panel has been closed
self.staff_area_page.click_staff_panel_close_button("staff-tools")
self.assertEqual(self.staff_area_page.selected_button_names, [])
self.assertEqual(self.staff_area_page.visible_staff_panels, [])
if __name__ == "__main__":
# Configure the screenshot directory
......
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