Commit 2232813d by Will Daly

Add tests for dynamic update of student training examples based on rubric changes.

Use dependency injection for event notification; moved some classes around.
Move the container JS classes into the Studio minified JS.
Fix a bug that broke student training updates in Firefox and PhantomJS.
Move fixture path installation into a common file shared by all tests.

Add tests that verify the correct notifications
are sent by the rubric view.
parent a05d51fd
...@@ -27,6 +27,7 @@ module.exports = function(config) { ...@@ -27,6 +27,7 @@ module.exports = function(config) {
'src/*.js', 'src/*.js',
'src/lms/*.js', 'src/lms/*.js',
'src/studio/*.js', 'src/studio/*.js',
'spec/test_shared.js',
'spec/*.js', 'spec/*.js',
'spec/lms/*.js', 'spec/lms/*.js',
'spec/studio/*.js', 'spec/studio/*.js',
......
...@@ -391,7 +391,7 @@ ...@@ -391,7 +391,7 @@
"submission_due": "2014-10-1T10:00:00", "submission_due": "2014-10-1T10:00:00",
"criteria": [ "criteria": [
{ {
"name": "52bfbd0eb3044212b809564866e77079", "name": "criterion_1",
"label": "Criterion with two options", "label": "Criterion with two options",
"prompt": "Prompt for criterion with two options", "prompt": "Prompt for criterion with two options",
"order_num": 0, "order_num": 0,
...@@ -400,14 +400,14 @@ ...@@ -400,14 +400,14 @@
{ {
"order_num": 0, "order_num": 0,
"points": 1, "points": 1,
"name": "85bbbecbb6a343f8a2146cde0e609ad0", "name": "option_1",
"label": "Fair", "label": "Fair",
"explanation": "Fair explanation" "explanation": "Fair explanation"
}, },
{ {
"order_num": 1, "order_num": 1,
"points": 2, "points": 2,
"name": "5936d5b9e281403ca123964055d4719a", "name": "option_2",
"label": "Good", "label": "Good",
"explanation": "Good explanation" "explanation": "Good explanation"
} }
...@@ -415,7 +415,7 @@ ...@@ -415,7 +415,7 @@
"points_possible": 2 "points_possible": 2
}, },
{ {
"name": "d96bb68a69ee4ccb8f86c753b6924f75", "name": "criterion_2",
"label": "Criterion with no options", "label": "Criterion with no options",
"prompt": "Prompt for criterion with no options", "prompt": "Prompt for criterion with no options",
"order_num": 0, "order_num": 0,
...@@ -424,7 +424,7 @@ ...@@ -424,7 +424,7 @@
"points_possible": 0 "points_possible": 0
}, },
{ {
"name": "2ca052403b06424da714f7a80dfb954d", "name": "criterion_3",
"label": "Criterion with optional feedback", "label": "Criterion with optional feedback",
"prompt": "Prompt for criterion with optional feedback", "prompt": "Prompt for criterion with optional feedback",
"order_num": 2, "order_num": 2,
...@@ -433,7 +433,7 @@ ...@@ -433,7 +433,7 @@
{ {
"order_num": 0, "order_num": 0,
"points": 2, "points": 2,
"name": "d7445661a89b4b339b9788cb7225a603", "name": "option_1",
"label": "Good", "label": "Good",
"explanation": "Good explanation" "explanation": "Good explanation"
} }
...@@ -462,6 +462,148 @@ ...@@ -462,6 +462,148 @@
"output": "oa_edit.html" "output": "oa_edit.html"
}, },
{ {
"template": "openassessmentblock/edit/oa_edit.html",
"context": {
"prompt": "Test prompt",
"title": "Test title",
"submission_due": "2014-10-1T10:00:00",
"criteria": [
{
"name": "criterion_with_two_options",
"label": "Criterion with two options",
"prompt": "Criterion with two options prompt",
"order_num": 0,
"feedback": "disabled",
"options": [
{
"order_num": 0,
"points": 1,
"name": "option_1",
"label": "Fair",
"explanation": "Fair explanation"
},
{
"order_num": 1,
"points": 2,
"name": "option_2",
"label": "Good",
"explanation": "Good explanation"
}
],
"points_possible": 2
},
{
"name": "criterion_no_options",
"label": "Criterion with no options",
"prompt": "Criterion with no options prompt",
"order_num": 0,
"options": [],
"feedback": "required",
"points_possible": 0
}
],
"assessments": {
"training": {
"examples": [
{
"answer": "Test answer",
"criteria": [
{
"name": "criterion_with_two_options",
"label": "Criterion with two options",
"prompt": "Criterion with two options prompt",
"order_num": 0,
"feedback": "disabled",
"options": [
{
"order_num": 0,
"points": 1,
"name": "option_1",
"label": "Fair",
"explanation": "Fair explanation"
},
{
"order_num": 1,
"points": 2,
"name": "option_2",
"label": "Good",
"explanation": "Good explanation"
}
],
"points_possible": 2,
"option_selected": "option_1"
},
{
"name": "criterion_no_options",
"label": "Criterion with no options",
"prompt": "Criterion with no options prompt",
"order_num": 0,
"options": [],
"feedback": "required",
"points_possible": 0,
"option_selected": ""
}
]
}
],
"template": {
"answer": "",
"criteria": [
{
"name": "criterion_with_two_options",
"label": "Criterion with two options",
"prompt": "Criterion with two options prompt",
"order_num": 0,
"feedback": "disabled",
"options": [
{
"order_num": 0,
"points": 1,
"name": "option_1",
"label": "Fair",
"explanation": "Fair explanation"
},
{
"order_num": 1,
"points": 2,
"name": "option_2",
"label": "Good",
"explanation": "Good explanation"
}
],
"points_possible": 2,
"option_selected": ""
},
{
"name": "criterion_no_options",
"label": "Criterion with no options",
"prompt": "Criterion with no options prompt",
"order_num": 0,
"options": [],
"feedback": "required",
"points_possible": 0,
"option_selected": ""
}
]
}
},
"peer_assessment": {
"start": "",
"due": "",
"must_grade": 5,
"must_be_graded_by": 3
}
},
"editor_assessments_order": [
"student_training",
"peer_assessment",
"self_assessment",
"example_based_assessment"
]
},
"output": "oa_edit_student_training.html"
},
{
"template": "openassessmentblock/staff_debug/staff_debug.html", "template": "openassessmentblock/staff_debug/staff_debug.html",
"context": { "context": {
"status_counts": { "status_counts": {
......
...@@ -56,7 +56,6 @@ describe("OpenAssessment.BaseView", function() { ...@@ -56,7 +56,6 @@ describe("OpenAssessment.BaseView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_base.html'); loadFixtures('oa_base.html');
// Create a new stub server // Create a new stub server
......
...@@ -42,7 +42,6 @@ describe("OpenAssessment.GradeView", function() { ...@@ -42,7 +42,6 @@ describe("OpenAssessment.GradeView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_grade_complete.html'); loadFixtures('oa_grade_complete.html');
// Create the stub server // Create the stub server
......
...@@ -44,7 +44,6 @@ describe("OpenAssessment.PeerView", function() { ...@@ -44,7 +44,6 @@ describe("OpenAssessment.PeerView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_peer_assessment.html'); loadFixtures('oa_peer_assessment.html');
// Create a new stub server // Create a new stub server
......
...@@ -97,7 +97,6 @@ describe("OpenAssessment.ResponseView", function() { ...@@ -97,7 +97,6 @@ describe("OpenAssessment.ResponseView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_response.html'); loadFixtures('oa_response.html');
// Create stub objects // Create stub objects
......
...@@ -39,7 +39,6 @@ describe("OpenAssessment.SelfView", function() { ...@@ -39,7 +39,6 @@ describe("OpenAssessment.SelfView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_self_assessment.html'); loadFixtures('oa_self_assessment.html');
// Create a new stub server // Create a new stub server
......
...@@ -69,9 +69,6 @@ describe("OpenAssessment.StaffInfoView", function() { ...@@ -69,9 +69,6 @@ describe("OpenAssessment.StaffInfoView", function() {
}; };
beforeEach(function() { beforeEach(function() {
// Configure the Jasmine fixtures path
jasmine.getFixtures().fixturesPath = 'base/fixtures';
// Create a new stub server // Create a new stub server
server = new StubServer(); server = new StubServer();
......
...@@ -45,7 +45,6 @@ describe("OpenAssessment.StudentTrainingView", function() { ...@@ -45,7 +45,6 @@ describe("OpenAssessment.StudentTrainingView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_student_training.html'); loadFixtures('oa_student_training.html');
// Create a new stub server // Create a new stub server
......
...@@ -7,7 +7,6 @@ describe("OpenAssessment.Rubric", function() { ...@@ -7,7 +7,6 @@ describe("OpenAssessment.Rubric", function() {
var rubric = null; var rubric = null;
beforeEach(function() { beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_rubric.html'); loadFixtures('oa_rubric.html');
var el = $("#peer-assessment--001__assessment").get(0); var el = $("#peer-assessment--001__assessment").get(0);
......
...@@ -52,28 +52,28 @@ describe("OpenAssessment.StudioView", function() { ...@@ -52,28 +52,28 @@ describe("OpenAssessment.StudioView", function() {
{ {
order_num: 0, order_num: 0,
label: "Criterion with two options", label: "Criterion with two options",
name: "52bfbd0eb3044212b809564866e77079", name: "criterion_1",
prompt: "Prompt for criterion with two options", prompt: "Prompt for criterion with two options",
feedback: "disabled", feedback: "disabled",
options: [ options: [
{ {
order_num: 0, order_num: 0,
points: 1, points: 1,
name: "85bbbecbb6a343f8a2146cde0e609ad0", name: "option_1",
label: "Fair", label: "Fair",
explanation: "Fair explanation" explanation: "Fair explanation"
}, },
{ {
order_num: 1, order_num: 1,
points: 2, points: 2,
name: "5936d5b9e281403ca123964055d4719a", name: "option_2",
label: "Good", label: "Good",
explanation: "Good explanation" explanation: "Good explanation"
} }
] ]
}, },
{ {
name: "d96bb68a69ee4ccb8f86c753b6924f75", name: "criterion_2",
label: "Criterion with no options", label: "Criterion with no options",
prompt: "Prompt for criterion with no options", prompt: "Prompt for criterion with no options",
order_num: 1, order_num: 1,
...@@ -81,7 +81,7 @@ describe("OpenAssessment.StudioView", function() { ...@@ -81,7 +81,7 @@ describe("OpenAssessment.StudioView", function() {
feedback: "required", feedback: "required",
}, },
{ {
name: "2ca052403b06424da714f7a80dfb954d", name: "criterion_3",
label: "Criterion with optional feedback", label: "Criterion with optional feedback",
prompt: "Prompt for criterion with optional feedback", prompt: "Prompt for criterion with optional feedback",
order_num: 2, order_num: 2,
...@@ -90,7 +90,7 @@ describe("OpenAssessment.StudioView", function() { ...@@ -90,7 +90,7 @@ describe("OpenAssessment.StudioView", function() {
{ {
order_num: 0, order_num: 0,
points: 2, points: 2,
name: "d7445661a89b4b339b9788cb7225a603", name: "option_1",
label: "Good", label: "Good",
explanation: "Good explanation" explanation: "Good explanation"
} }
...@@ -121,7 +121,6 @@ describe("OpenAssessment.StudioView", function() { ...@@ -121,7 +121,6 @@ describe("OpenAssessment.StudioView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html'); loadFixtures('oa_edit.html');
// Create the stub server // Create the stub server
...@@ -190,7 +189,7 @@ describe("OpenAssessment.StudioView", function() { ...@@ -190,7 +189,7 @@ describe("OpenAssessment.StudioView", function() {
expect(runtime.notify).toHaveBeenCalledWith('cancel', {}); expect(runtime.notify).toHaveBeenCalledWith('cancel', {});
}); });
it("displays an error when server reports an update XML error", function() { it("displays an error when server reports an error", function() {
server.updateError = true; server.updateError = true;
view.save(); view.save();
expect(runtime.notify).toHaveBeenCalledWith('error', {msg: 'Test error'}); expect(runtime.notify).toHaveBeenCalledWith('error', {msg: 'Test error'});
......
...@@ -18,7 +18,6 @@ describe("OpenAssessment edit assessment views", function() { ...@@ -18,7 +18,6 @@ describe("OpenAssessment edit assessment views", function() {
}; };
beforeEach(function() { beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html'); loadFixtures('oa_edit.html');
}); });
...@@ -126,4 +125,4 @@ describe("OpenAssessment edit assessment views", function() { ...@@ -126,4 +125,4 @@ describe("OpenAssessment edit assessment views", function() {
it("Enables and disables", function() { testEnableAndDisable(view); }); it("Enables and disables", function() { testEnableAndDisable(view); });
it("Loads a description", function() { testLoadXMLExamples(view); }); it("Loads a description", function() { testLoadXMLExamples(view); });
}); });
}); });
\ No newline at end of file
/**
Tests for the student training listener,
which dynamically updates student training examples
based on rubric changes.
**/
describe("OpenAssessment.StudentTrainingListener", function() {
var listener = null;
/**
Check that all student training examples have the expected
criteria or option labels.
Args:
actual (array): A list of example criteria or option labels
(object literals) retrieved from the DOM.
expected (object literal): The expected value for each example.
numExamples (int, optional): The number of student training examples
(defaults to 1).
**/
var assertExampleLabels = function(actual, expected, numExamples) {
// The most common case is one example, so use that as a default.
if (typeof(numExamples) == "undefined") {
numExamples = 1;
}
// Add one to the number of examples to include the client-side template.
expect(actual.length).toEqual(numExamples + 1);
// Verify that each example matches what we expect.
// Since there is only one rubric for the problem,
// the training examples should always match that rubric.
for (var index in actual) {
for (var criterionName in expected) {
expect(actual[index][criterionName]).toEqual(expected[criterionName]);
}
}
};
beforeEach(function() {
loadFixtures('oa_edit_student_training.html');
listener = new OpenAssessment.StudentTrainingListener();
});
it("updates the label of an option", function() {
// Initial state, set by the fixture
assertExampleLabels(
listener.examplesOptionsLabels(),
{
criterion_with_two_options: {
"": "Not Scored",
option_1: "Fair - 1 points",
option_2: "Good - 2 points"
}
}
);
// Update the option label and points,
listener.optionUpdated({
criterionName: "criterion_with_two_options",
name: "option_1",
label: "This is a new label!",
points: 42
});
// Verify the new state
assertExampleLabels(
listener.examplesOptionsLabels(),
{
criterion_with_two_options: {
"": "Not Scored",
option_1: "This is a new label! - 42 points",
option_2: "Good - 2 points"
}
}
);
});
it("removes an option and displays an alert", function() {
// Initial state, set by the fixture
assertExampleLabels(
listener.examplesOptionsLabels(),
{
criterion_with_two_options: {
"": "Not Scored",
option_1: "Fair - 1 points",
option_2: "Good - 2 points"
}
}
);
expect(listener.alert.isVisible()).toBe(false);
// Remove an option
listener.optionRemove({
criterionName: "criterion_with_two_options",
name: "option_1"
});
// Verify the new state
assertExampleLabels(
listener.examplesOptionsLabels(),
{
criterion_with_two_options: {
"": "Not Scored",
option_2: "Good - 2 points"
}
}
);
// The alert should be displayed
expect(listener.alert.isVisible()).toBe(true);
});
it("removes a criterion if the criterion has no options", function() {
// Initial state, set by the fixture
assertExampleLabels(
listener.examplesCriteriaLabels(),
{ criterion_with_two_options: "Criterion with two options" }
);
expect(listener.alert.isVisible()).toBe(false);
// Remove all options for the criterion
listener.removeAllOptions({
criterionName: "criterion_with_two_options"
});
// Since the criterion has no options, it should no longer
// be available in student training
assertExampleLabels(listener.examplesCriteriaLabels(), {}, 1);
// The alert should be displayed
expect(listener.alert.isVisible()).toBe(true);
});
it("updates the label of a criterion", function() {
// Initial state, set by the fixture
assertExampleLabels(
listener.examplesCriteriaLabels(),
{ criterion_with_two_options: "Criterion with two options" }
);
// Update a label
listener.criterionUpdated({
criterionName: "criterion_with_two_options",
criterionLabel: "This is a new label!",
});
// Verify the new state
assertExampleLabels(
listener.examplesCriteriaLabels(),
{ criterion_with_two_options: "This is a new label!" }
);
});
it("adds a criterion and options", function() {
// Initial state, set by the fixture
assertExampleLabels(
listener.examplesCriteriaLabels(),
{ criterion_with_two_options: "Criterion with two options" }
);
// Add the criterion, which has no options
listener.criterionAdd({
criterionName: "new_criterion",
label: "This is a new criterion!"
});
// Since the criterion has no options, it should not
// be displayed.
assertExampleLabels(
listener.examplesCriteriaLabels(),
{ criterion_with_two_options: "Criterion with two options" }
);
// Add an option to the criterion
listener.optionAdd({
criterionName: "new_criterion",
name: "new_option",
label: "This is a new option!",
points: 56
});
// Now the criterion should be visible in student training
assertExampleLabels(
listener.examplesCriteriaLabels(),
{
criterion_with_two_options: "Criterion with two options",
new_criterion: "This is a new criterion!"
}
);
assertExampleLabels(
listener.examplesOptionsLabels(),
{
criterion_with_two_options: {
"": "Not Scored",
option_1: "Fair - 1 points",
option_2: "Good - 2 points",
},
new_criterion: {
"": "Not Scored",
new_option: "This is a new option! - 56 points"
}
}
);
// Add another option to the criterion
listener.optionAdd({
criterionName: "new_criterion",
name: "yet_another_option",
label: "This is yet another option!",
points: 27
});
assertExampleLabels(
listener.examplesOptionsLabels(),
{
criterion_with_two_options: {
"": "Not Scored",
option_1: "Fair - 1 points",
option_2: "Good - 2 points",
},
new_criterion: {
"": "Not Scored",
new_option: "This is a new option! - 56 points",
yet_another_option: "This is yet another option! - 27 points"
}
}
);
});
it("removes a criterion and displays an alert", function() {
// Initial state, set by the fixture
assertExampleLabels(
listener.examplesCriteriaLabels(),
{ criterion_with_two_options: "Criterion with two options" }
);
expect(listener.alert.isVisible()).toBe(false);
// Remove the criterion
listener.criterionRemove({
criterionName: "criterion_with_two_options"
});
// The criterion should no longer be displayed
assertExampleLabels(listener.examplesCriteriaLabels(), {}, 1);
// The alert should be displayed
expect(listener.alert.isVisible()).toBe(true);
});
});
describe("OpenAssessment.Notifier", function() {
var notifier = null;
var listeners = [];
var StubListener = function() {
this.receivedData = null;
this.testNotification = function(data) {
this.receivedData = data;
};
};
beforeEach(function() {
listeners = [ new StubListener(), new StubListener() ];
notifier = new OpenAssessment.Notifier(listeners);
});
it("notifies listeners when a notification fires", function() {
// Fire a notification that the listeners don't respond to
notifier.notificationFired("ignore this!", {});
expect(listeners[0].receivedData).toBe(null);
expect(listeners[1].receivedData).toBe(null);
// Fire a notification that the listeners care about
var testData = { foo: "bar" };
notifier.notificationFired("testNotification", testData);
// Check that the listeners were notified
expect(listeners[0].receivedData).toBe(testData);
expect(listeners[1].receivedData).toBe(testData);
});
});
...@@ -8,7 +8,6 @@ describe("OpenAssessment.EditPromptView", function() { ...@@ -8,7 +8,6 @@ describe("OpenAssessment.EditPromptView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html'); loadFixtures('oa_edit.html');
// Create the view // Create the view
...@@ -22,4 +21,4 @@ describe("OpenAssessment.EditPromptView", function() { ...@@ -22,4 +21,4 @@ describe("OpenAssessment.EditPromptView", function() {
view.promptText("This is a test prompt!"); view.promptText("This is a test prompt!");
expect(view.promptText()).toEqual("This is a test prompt!"); expect(view.promptText()).toEqual("This is a test prompt!");
}); });
}); });
\ No newline at end of file
...@@ -3,13 +3,25 @@ Tests for the rubric editing view. ...@@ -3,13 +3,25 @@ Tests for the rubric editing view.
**/ **/
describe("OpenAssessment.EditRubricView", function() { describe("OpenAssessment.EditRubricView", function() {
// Use a stub notifier implementation that simply stores
// the notifications it receives.
var notifier = null;
var StubNotifier = function() {
this.notifications = [];
this.notificationFired = function(name, data) {
this.notifications.push({
name: name,
data: data
});
};
};
var view = null; var view = null;
beforeEach(function() { beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html'); loadFixtures('oa_edit.html');
var el = $("#oa_rubric_editor_wrapper").get(0); var el = $("#oa_rubric_editor_wrapper").get(0);
view = new OpenAssessment.EditRubricView(el); notifier = new StubNotifier();
view = new OpenAssessment.EditRubricView(el, notifier);
}); });
it("reads a criteria definition from the editor", function() { it("reads a criteria definition from the editor", function() {
...@@ -20,7 +32,7 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -20,7 +32,7 @@ describe("OpenAssessment.EditRubricView", function() {
// Criterion with two options, feedback disabled // Criterion with two options, feedback disabled
expect(criteria[0]).toEqual({ expect(criteria[0]).toEqual({
name: "52bfbd0eb3044212b809564866e77079", name: "criterion_1",
label: "Criterion with two options", label: "Criterion with two options",
prompt: "Prompt for criterion with two options", prompt: "Prompt for criterion with two options",
order_num: 0, order_num: 0,
...@@ -29,14 +41,14 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -29,14 +41,14 @@ describe("OpenAssessment.EditRubricView", function() {
{ {
order_num: 0, order_num: 0,
points: 1, points: 1,
name: "85bbbecbb6a343f8a2146cde0e609ad0", name: "option_1",
label: "Fair", label: "Fair",
explanation: "Fair explanation" explanation: "Fair explanation"
}, },
{ {
order_num: 1, order_num: 1,
points: 2, points: 2,
name: "5936d5b9e281403ca123964055d4719a", name: "option_2",
label: "Good", label: "Good",
explanation: "Good explanation" explanation: "Good explanation"
} }
...@@ -45,7 +57,7 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -45,7 +57,7 @@ describe("OpenAssessment.EditRubricView", function() {
// Criterion with no options, feedback required // Criterion with no options, feedback required
expect(criteria[1]).toEqual({ expect(criteria[1]).toEqual({
name: "d96bb68a69ee4ccb8f86c753b6924f75", name: "criterion_2",
label: "Criterion with no options", label: "Criterion with no options",
prompt: "Prompt for criterion with no options", prompt: "Prompt for criterion with no options",
order_num: 1, order_num: 1,
...@@ -55,7 +67,7 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -55,7 +67,7 @@ describe("OpenAssessment.EditRubricView", function() {
// Criterion with one option, feeback optional // Criterion with one option, feeback optional
expect(criteria[2]).toEqual({ expect(criteria[2]).toEqual({
name: "2ca052403b06424da714f7a80dfb954d", name: "criterion_3",
label: "Criterion with optional feedback", label: "Criterion with optional feedback",
prompt: "Prompt for criterion with optional feedback", prompt: "Prompt for criterion with optional feedback",
order_num: 2, order_num: 2,
...@@ -64,7 +76,7 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -64,7 +76,7 @@ describe("OpenAssessment.EditRubricView", function() {
{ {
order_num: 0, order_num: 0,
points: 2, points: 2,
name: "d7445661a89b4b339b9788cb7225a603", name: "option_1",
label: "Good", label: "Good",
explanation: "Good explanation" explanation: "Good explanation"
} }
...@@ -75,14 +87,13 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -75,14 +87,13 @@ describe("OpenAssessment.EditRubricView", function() {
it("creates new criteria and options", function() { it("creates new criteria and options", function() {
// Delete all existing criteria from the rubric // Delete all existing criteria from the rubric
// Then add new criteria (created from a client-side template) // Then add new criteria (created from a client-side template)
view.removeAllCriteria(); $.each(view.getAllCriteria(), function() { view.removeCriterion(this); });
view.addCriterion(); view.addCriterion();
view.addCriterion(); view.addCriterion();
// Add an option to the second criterion // Add an option to the second criterion
view.getCriterionItem(1).addOption(); view.addOption(1);
// Check the definition
// Since no criteria/option names are set, leave them out of the description. // Since no criteria/option names are set, leave them out of the description.
// This will cause the server to assign them unique names. // This will cause the server to assign them unique names.
var criteria = view.criteriaDefinition(); var criteria = view.criteriaDefinition();
...@@ -124,4 +135,77 @@ describe("OpenAssessment.EditRubricView", function() { ...@@ -124,4 +135,77 @@ describe("OpenAssessment.EditRubricView", function() {
expect(view.feedbackPrompt()).toEqual(prompt); expect(view.feedbackPrompt()).toEqual(prompt);
}); });
});
\ No newline at end of file it("fires a notification when an option is added", function() {
view.addOption();
expect(notifier.notifications).toContain({
name: "optionAdd",
data: {
criterionName: 'criterion_1',
criterionLabel: 'Criterion with two options',
name:'0',
label: '',
points : 1
}
});
// Add a second option and ensure that it is given a unique name
view.addOption();
expect(notifier.notifications).toContain({
name: "optionAdd",
data: {
criterionName: 'criterion_1',
criterionLabel: 'Criterion with two options',
name:'1',
label: '',
points : 1
}
});
});
it("fires a notification when an option is removed", function() {
view.removeOption(0, view.getOptionItem(0, 0));
expect(notifier.notifications).toContain({
name: "optionRemove",
data: {
criterionName: 'criterion_1',
name: 'option_1'
}
});
});
it("fires a notification when an option's label or points are updated", function() {
// Simulate what happens when the options label or points are updated
view.getOptionItem(0, 0).updateHandler();
expect(notifier.notifications).toContain({
name: "optionUpdated",
data: {
criterionName: 'criterion_1',
name: 'option_1',
label: 'Fair',
points: 1
}
});
});
it("fires a notification when a criterion's label is updated", function() {
// Simulate what happens when a criterion label is updated
view.getCriterionItem(0).updateHandler();
expect(notifier.notifications).toContain({
name: "criterionUpdated",
data: {
criterionName: 'criterion_1',
criterionLabel: 'Criterion with two options'
}
});
});
it("fires a notification when a criterion is removed", function() {
view.criteriaContainer.remove(view.getCriterionItem(0));
expect(notifier.notifications).toContain({
name: "criterionRemove",
data: {criterionName : 'criterion_1'}
});
});
});
...@@ -22,7 +22,6 @@ describe("OpenAssessment.EditSettingsView", function() { ...@@ -22,7 +22,6 @@ describe("OpenAssessment.EditSettingsView", function() {
beforeEach(function() { beforeEach(function() {
// Load the DOM fixture // Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html'); loadFixtures('oa_edit.html');
// Create the stub assessment views // Create the stub assessment views
......
describe("OpenAssessment.ValidationAlert", function() {
var alert = null;
beforeEach(function() {
loadFixtures('oa_edit.html');
alert = new OpenAssessment.ValidationAlert(
$("#openassessment_rubric_validation_alert")
);
});
it("shows and hides an alert", function() {
// Initially, the alert should be hidden
expect(alert.isVisible()).toBe(false);
// Show the alert
alert.show();
expect(alert.isVisible()).toBe(true);
// Hide the alert
alert.hide();
expect(alert.isVisible()).toBe(false);
});
it("sets the alert title and message", function() {
// Set the title and message
alert.setMessage("new title", "new message");
expect(alert.getTitle()).toEqual("new title");
expect(alert.getMessage()).toEqual("new message");
});
});
\ No newline at end of file
/**
Common test configuration, loaded before any of the spec files.
**/
// Set the fixture path
jasmine.getFixtures().fixturesPath = 'base/fixtures';
...@@ -44,8 +44,8 @@ including for pre-existing items in the container element. ...@@ -44,8 +44,8 @@ including for pre-existing items in the container element.
Templates elements are never deleted, so they should be hidden using CSS styles. Templates elements are never deleted, so they should be hidden using CSS styles.
Args: Args:
containerItem (object): The container item object used to access containerItem (constructor): The constructor of the container item object
the contents of items in the container. used to access the contents of items in the container.
Kwargs: Kwargs:
containerElement (DOM element): The element representing the container. containerElement (DOM element): The element representing the container.
...@@ -55,25 +55,33 @@ Kwargs: ...@@ -55,25 +55,33 @@ Kwargs:
There may be one of these for each item in the container. There may be one of these for each item in the container.
containerItemClass (string): The CSS class of items in the container. containerItemClass (string): The CSS class of items in the container.
New items will be assigned this class. New items will be assigned this class.
notifier (OpenAssessment.Notifier): Used to send notifications of updates to container items.
**/ **/
OpenAssessment.Container = function(containerItem, kwargs) { OpenAssessment.Container = function(containerItem, kwargs) {
this.containerItem = containerItem;
this.containerElement = kwargs.containerElement; this.containerElement = kwargs.containerElement;
this.templateElement = kwargs.templateElement; this.templateElement = kwargs.templateElement;
this.addButtonElement = kwargs.addButtonElement; this.addButtonElement = kwargs.addButtonElement;
this.removeButtonClass = kwargs.removeButtonClass; this.removeButtonClass = kwargs.removeButtonClass;
this.containerItemClass = kwargs.containerItemClass; this.containerItemClass = kwargs.containerItemClass;
this.notifier = kwargs.notifier;
// Since every container item should be instantiated with
// the notifier we were given, create a helper method
// that does this automatically.
var container = this;
this.createContainerItem = function(element) {
return new containerItem(element, container.notifier);
};
// Install a click handler for the add button // Install a click handler for the add button
$(this.addButtonElement).click($.proxy(this.add, this)); $(this.addButtonElement).click($.proxy(this.add, this));
// Find items already in the container and install click // Find items already in the container and install click
// handlers for the delete buttons. // handlers for the delete buttons.
var container = this;
$("." + this.removeButtonClass, this.containerElement).click( $("." + this.removeButtonClass, this.containerElement).click(
function(eventData) { function(eventData) {
var item = new container.containerItem(eventData.target); var item = container.createContainerItem(eventData.target);
container.remove(item); container.remove(item);
} }
); );
...@@ -82,7 +90,7 @@ OpenAssessment.Container = function(containerItem, kwargs) { ...@@ -82,7 +90,7 @@ OpenAssessment.Container = function(containerItem, kwargs) {
// own event handlers. // own event handlers.
$("." + this.containerItemClass, this.containerElement).each( $("." + this.containerItemClass, this.containerElement).each(
function(index, element) { function(index, element) {
new container.containerItem(element); container.createContainerItem(element);
} }
); );
}; };
...@@ -112,13 +120,13 @@ OpenAssessment.Container.prototype = { ...@@ -112,13 +120,13 @@ OpenAssessment.Container.prototype = {
var containerItem = $("." + this.containerItemClass, this.containerElement).last(); var containerItem = $("." + this.containerItemClass, this.containerElement).last();
containerItem.find('.' + this.removeButtonClass) containerItem.find('.' + this.removeButtonClass)
.click(function(eventData) { .click(function(eventData) {
var containerItem = new container.containerItem(eventData.target); var containerItem = container.createContainerItem(eventData.target);
container.remove(containerItem); container.remove(containerItem);
} ); } );
// Initialize the item, allowing it to install event handlers. // Initialize the item, allowing it to install event handlers.
// Fire event handler for adding a new element // Fire event handler for adding a new element
var handlerItem = new this.containerItem(containerItem); var handlerItem = container.createContainerItem(containerItem);
handlerItem.addHandler(); handlerItem.addHandler();
}, },
...@@ -133,7 +141,7 @@ OpenAssessment.Container.prototype = { ...@@ -133,7 +141,7 @@ OpenAssessment.Container.prototype = {
**/ **/
remove: function(item) { remove: function(item) {
var itemElement = $(item.element).closest("." + this.containerItemClass); var itemElement = $(item.element).closest("." + this.containerItemClass);
var containerItem = new this.containerItem(itemElement); var containerItem = this.createContainerItem(itemElement);
containerItem.removeHandler(); containerItem.removeHandler();
itemElement.remove(); itemElement.remove();
}, },
...@@ -152,7 +160,7 @@ OpenAssessment.Container.prototype = { ...@@ -152,7 +160,7 @@ OpenAssessment.Container.prototype = {
$("." + this.containerItemClass, this.containerElement).each( $("." + this.containerItemClass, this.containerElement).each(
function(index, element) { function(index, element) {
var containerItem = new container.containerItem(element); var containerItem = container.createContainerItem(element);
var fieldValues = containerItem.getFieldValues(); var fieldValues = containerItem.getFieldValues();
values.push(fieldValues); values.push(fieldValues);
} }
...@@ -173,7 +181,7 @@ OpenAssessment.Container.prototype = { ...@@ -173,7 +181,7 @@ OpenAssessment.Container.prototype = {
**/ **/
getItem: function(index) { getItem: function(index) {
var element = $("." + this.containerItemClass, this.containerElement).get(index); var element = $("." + this.containerItemClass, this.containerElement).get(index);
return (element !== undefined) ? new this.containerItem(element) : null; return (element !== undefined) ? this.createContainerItem(element) : null;
}, },
/** /**
...@@ -186,6 +194,6 @@ OpenAssessment.Container.prototype = { ...@@ -186,6 +194,6 @@ OpenAssessment.Container.prototype = {
getAllItems: function() { getAllItems: function() {
var container = this; var container = this;
return $("." + this.containerItemClass, this.containerElement) return $("." + this.containerItemClass, this.containerElement)
.map(function() { return new container.containerItem(this); }); .map(function() { return container.createContainerItem(this); });
} }
}; };
...@@ -15,7 +15,7 @@ OpenAssessment.ItemUtilities = { ...@@ -15,7 +15,7 @@ OpenAssessment.ItemUtilities = {
createUniqueName: function(selector, nameAttribute) { createUniqueName: function(selector, nameAttribute) {
var index = 0; var index = 0;
while (index <= selector.length) { while (index <= selector.length) {
if (selector.parent().find("*[" + nameAttribute + "='" + index + "']").length == 0) { if (selector.parent().find("*[" + nameAttribute + "='" + index + "']").length === 0) {
return index.toString(); return index.toString();
} }
index++; index++;
...@@ -30,13 +30,14 @@ container object. Constructs a new RubricOption element. ...@@ -30,13 +30,14 @@ container object. Constructs a new RubricOption element.
Args: Args:
element (OpenAssessment.Container): The container that the option is a member of. element (OpenAssessment.Container): The container that the option is a member of.
notifier (OpenAssessment.Notifier): Used to send notifications of updates to rubric options.
Returns: Returns:
OpenAssessment.RubricOption OpenAssessment.RubricOption
**/ **/
OpenAssessment.RubricOption = function(element) { OpenAssessment.RubricOption = function(element, notifier) {
this.element = element; this.element = element;
this.modificationHandler = new OpenAssessment.RubricEventHandler(); this.notifier = notifier;
$(this.element).focusout($.proxy(this.updateHandler, this)); $(this.element).focusout($.proxy(this.updateHandler, this));
}; };
...@@ -78,7 +79,7 @@ OpenAssessment.RubricOption.prototype = { ...@@ -78,7 +79,7 @@ OpenAssessment.RubricOption.prototype = {
}, },
/** /**
Hook into the event handler for removal of a criterion option. Hook into the event handler for addition of a criterion option.
*/ */
addHandler: function (){ addHandler: function (){
...@@ -97,7 +98,7 @@ OpenAssessment.RubricOption.prototype = { ...@@ -97,7 +98,7 @@ OpenAssessment.RubricOption.prototype = {
$(".openassessment_criterion_option_name", this.element).attr("value", name); $(".openassessment_criterion_option_name", this.element).attr("value", name);
var fields = this.getFieldValues(); var fields = this.getFieldValues();
this.modificationHandler.notificationFired( this.notifier.notificationFired(
"optionAdd", "optionAdd",
{ {
"criterionName": criterionName, "criterionName": criterionName,
...@@ -116,7 +117,7 @@ OpenAssessment.RubricOption.prototype = { ...@@ -116,7 +117,7 @@ OpenAssessment.RubricOption.prototype = {
removeHandler: function (){ removeHandler: function (){
var criterionName = $(this.element).data('criterion'); var criterionName = $(this.element).data('criterion');
var optionName = $(this.element).data('option'); var optionName = $(this.element).data('option');
this.modificationHandler.notificationFired( this.notifier.notificationFired(
"optionRemove", "optionRemove",
{ {
"criterionName": criterionName, "criterionName": criterionName,
...@@ -136,7 +137,7 @@ OpenAssessment.RubricOption.prototype = { ...@@ -136,7 +137,7 @@ OpenAssessment.RubricOption.prototype = {
var optionName = $(this.element).data('option'); var optionName = $(this.element).data('option');
var optionLabel = fields.label; var optionLabel = fields.label;
var optionPoints = fields.points; var optionPoints = fields.points;
this.modificationHandler.notificationFired( this.notifier.notificationFired(
"optionUpdated", "optionUpdated",
{ {
"criterionName": criterionName, "criterionName": criterionName,
...@@ -154,20 +155,22 @@ the DOM. ...@@ -154,20 +155,22 @@ the DOM.
Args: Args:
element (JQuery Object): The selection which describes the scope of the criterion. element (JQuery Object): The selection which describes the scope of the criterion.
notifier (OpenAssessment.Notifier): Used to send notifications of updates to rubric criteria.
Returns: Returns:
OpenAssessment.RubricCriterion OpenAssessment.RubricCriterion
**/ **/
OpenAssessment.RubricCriterion = function(element) { OpenAssessment.RubricCriterion = function(element, notifier) {
this.element = element; this.element = element;
this.modificationHandler = new OpenAssessment.RubricEventHandler(); this.notifier = notifier;
this.optionContainer = new OpenAssessment.Container( this.optionContainer = new OpenAssessment.Container(
OpenAssessment.RubricOption, { OpenAssessment.RubricOption, {
containerElement: $(".openassessment_criterion_option_list", this.element).get(0), containerElement: $(".openassessment_criterion_option_list", this.element).get(0),
templateElement: $("#openassessment_option_template").get(0), templateElement: $("#openassessment_option_template").get(0),
addButtonElement: $(".openassessment_criterion_add_option", this.element).get(0), addButtonElement: $(".openassessment_criterion_add_option", this.element).get(0),
removeButtonClass: "openassessment_criterion_option_remove_button", removeButtonClass: "openassessment_criterion_option_remove_button",
containerItemClass: "openassessment_criterion_option" containerItemClass: "openassessment_criterion_option",
notifier: this.notifier
} }
); );
...@@ -247,7 +250,7 @@ OpenAssessment.RubricCriterion.prototype = { ...@@ -247,7 +250,7 @@ OpenAssessment.RubricCriterion.prototype = {
*/ */
removeHandler: function(){ removeHandler: function(){
var criterionName = $(this.element).data('criterion'); var criterionName = $(this.element).data('criterion');
this.modificationHandler.notificationFired("criterionRemove", {'criterionName': criterionName}); this.notifier.notificationFired("criterionRemove", {'criterionName': criterionName});
}, },
/** /**
...@@ -258,7 +261,7 @@ OpenAssessment.RubricCriterion.prototype = { ...@@ -258,7 +261,7 @@ OpenAssessment.RubricCriterion.prototype = {
var fields = this.getFieldValues(); var fields = this.getFieldValues();
var criterionName = fields.name; var criterionName = fields.name;
var criterionLabel = fields.label; var criterionLabel = fields.label;
this.modificationHandler.notificationFired( this.notifier.notificationFired(
"criterionUpdated", "criterionUpdated",
{'criterionName': criterionName, 'criterionLabel': criterionLabel} {'criterionName': criterionName, 'criterionLabel': criterionLabel}
); );
...@@ -270,10 +273,11 @@ OpenAssessment.RubricCriterion.prototype = { ...@@ -270,10 +273,11 @@ OpenAssessment.RubricCriterion.prototype = {
The TrainingExample class is used to construct and retrieve information from its element within the DOM The TrainingExample class is used to construct and retrieve information from its element within the DOM
Args: Args:
element (JQuery Object): the selection which identifies the scope of the training example. element (JQuery Object): the selection which identifies the scope of the training example.
Returns: Returns:
OpenAssessment.TrainingExample OpenAssessment.TrainingExample
**/ **/
OpenAssessment.TrainingExample = function(element){ OpenAssessment.TrainingExample = function(element){
this.element = element; this.element = element;
...@@ -304,5 +308,4 @@ OpenAssessment.TrainingExample.prototype = { ...@@ -304,5 +308,4 @@ OpenAssessment.TrainingExample.prototype = {
addHandler: function() {}, addHandler: function() {},
removeHandler: function() {}, removeHandler: function() {},
updateHandler: function() {} updateHandler: function() {}
}; };
\ No newline at end of file
...@@ -52,7 +52,10 @@ OpenAssessment.StudioView = function(runtime, element, server) { ...@@ -52,7 +52,10 @@ OpenAssessment.StudioView = function(runtime, element, server) {
// Initialize the rubric tab view // Initialize the rubric tab view
this.rubricView = new OpenAssessment.EditRubricView( this.rubricView = new OpenAssessment.EditRubricView(
$("#oa_rubric_editor_wrapper", this.element).get(0) $("#oa_rubric_editor_wrapper", this.element).get(0),
new OpenAssessment.Notifier([
new OpenAssessment.StudentTrainingListener()
])
); );
// Install the save and cancel buttons // Install the save and cancel buttons
......
...@@ -280,7 +280,7 @@ OpenAssessment.EditStudentTrainingView = function(element) { ...@@ -280,7 +280,7 @@ OpenAssessment.EditStudentTrainingView = function(element) {
removeButtonClass: "openassessment_training_example_remove", removeButtonClass: "openassessment_training_example_remove",
containerItemClass: "openassessment_training_example" containerItemClass: "openassessment_training_example"
} }
) );
}; };
OpenAssessment.EditStudentTrainingView.prototype = { OpenAssessment.EditStudentTrainingView.prototype = {
...@@ -333,21 +333,6 @@ OpenAssessment.EditStudentTrainingView.prototype = { ...@@ -333,21 +333,6 @@ OpenAssessment.EditStudentTrainingView.prototype = {
}, },
/** /**
Get or set the XML defining the training examples.
Args:
xml (string, optional): The XML of the training example definitions.
Returns:
string
**/
exampleDefinitions: function(xml) {
var sel = $("#student_training_examples", this.element);
return OpenAssessment.Fields.stringField(sel, xml);
},
/**
Gets the ID of the assessment Gets the ID of the assessment
Returns: Returns:
......
/**
Notify multiple listeners that an event has occurred.
A listener is any object that implements a notification method.
For example, a listener for the notification "foo" might look like:
>>> var fooListener = {
>>> foo: function(data) {};
>>> };
Since `fooListener` implements `foo`, it will be notified when
a "foo" notification fires.
All notification methods must take a single argument, "data",
which is contains arbitrary information associated with the notification.
If a notification is fired that the listener does not respond to,
the listener will ignore the notification.
Args:
listeners (array): List of objects
**/
OpenAssessment.Notifier = function(listeners) {
this.listeners = listeners;
};
OpenAssessment.Notifier.prototype = {
/**
Fire a notification, which will be received
Args:
name (string): The name of the notification. This should
be the same as the name of the method implemented
by the listeners.
data (object literal): Arbitrary data to include with the notification.
**/
notificationFired: function(name, data) {
for (var i = 0; i < this.listeners.length; i++) {
if (typeof(this.listeners[i][name]) === 'function') {
this.listeners[i][name](data);
}
}
}
};
\ No newline at end of file
/** /**
Interface for editing rubric definitions. Interface for editing rubric definitions.
Args:
element (DOM element): The DOM element representing the rubric.
notifier (OpenAssessment.Notifier): Used to notify other views about updates to the rubric.
This view fires the following notification events:
* optionAdd: An option was added to the rubric.
* optionRemove: An option was removed from the rubric.
* optionUpdated: An option's label and/or points were updated in the rubric.
* criterionRemove: A criterion was removed from the rubric.
* criterionUpdated: A criterion's label was updated in the rubric.
**/ **/
OpenAssessment.EditRubricView = function(element) { OpenAssessment.EditRubricView = function(element, notifier) {
this.element = element; this.element = element;
this.criteriaContainer = new OpenAssessment.Container( this.criteriaContainer = new OpenAssessment.Container(
OpenAssessment.RubricCriterion, { OpenAssessment.RubricCriterion, {
containerElement: $("#openassessment_criterion_list", this.element).get(0), containerElement: $("#openassessment_criterion_list", this.element).get(0),
templateElement: $("#openassessment_criterion_template", this.element).get(0), templateElement: $("#openassessment_criterion_template", this.element).get(0),
addButtonElement: $("#openassessment_rubric_add_criterion", this.element).get(0), addButtonElement: $("#openassessment_rubric_add_criterion", this.element).get(0),
removeButtonClass: "openassessment_criterion_remove_button", removeButtonClass: "openassessment_criterion_remove_button",
containerItemClass: "openassessment_criterion" containerItemClass: "openassessment_criterion",
notifier: notifier
} }
); );
this.alert = new OpenAssessment.ValidationAlert($('#openassessment_rubric_validation_alert', this.element)); this.alert = new OpenAssessment.ValidationAlert($('#openassessment_rubric_validation_alert', this.element));
...@@ -78,16 +92,6 @@ OpenAssessment.EditRubricView.prototype = { ...@@ -78,16 +92,6 @@ OpenAssessment.EditRubricView.prototype = {
}, },
/** /**
Remove all criteria in this rubric.
Mainly useful for testing.
**/
removeAllCriteria: function() {
var items = this.criteriaContainer.getAllItems();
var view = this;
$.each(items, function() { view.criteriaContainer.remove(this); });
},
/**
Add a new criterion to the rubric. Add a new criterion to the rubric.
Uses a client-side template to create the new criterion. Uses a client-side template to create the new criterion.
**/ **/
...@@ -96,64 +100,94 @@ OpenAssessment.EditRubricView.prototype = { ...@@ -96,64 +100,94 @@ OpenAssessment.EditRubricView.prototype = {
}, },
/** /**
Retrieve a criterion item (a container item) from the rubric Remove a criterion from the rubric.
at a particular index.
Args:
item (OpenAssessment.RubricCriterion): The criterion item to remove.
**/
removeCriterion: function(item) {
this.criteriaContainer.remove(item);
},
/**
Retrieve all criteria from the rubric.
Returns:
Array of OpenAssessment.RubricCriterion objects.
**/
getAllCriteria: function() {
return this.criteriaContainer.getAllItems();
},
/**
Retrieve a criterion item from the rubric.
Args: Args:
index (int): The index of the criterion, starting from 0. index (int): The index of the criterion, starting from 0.
Returns: Returns:
OpenAssessment.RubricCriterion OpenAssessment.RubricCriterion or null
**/ **/
getCriterionItem: function(index) { getCriterionItem: function(index) {
return this.criteriaContainer.getItem(index); return this.criteriaContainer.getItem(index);
} },
};
/**
A class which controls the validation alert which we place at the top of the rubric page after
changes are made which will propagate to the settings section.
Args: /**
element (element): The element that specifies the div that the validation consists of. Add a new option to the rubric.
Returns: Args:
Openassessment.ValidationAlert criterionIndex (int): The index of the criterion to which
*/ the option will be added (starts from 0).
OpenAssessment.ValidationAlert = function (element) {
this.element = element;
this.title = $(".openassessment_alert_title", this.element);
this.message = $(".openassessment_alert_message", this.element);
};
OpenAssessment.ValidationAlert.prototype = { **/
addOption: function(criterionIndex) {
var criterionItem = this.getCriterionItem(criterionIndex);
criterionItem.optionContainer.add();
},
/** /**
Hides the alert. Remove an option from the rubric.
*/
hide: function () { Args:
this.element.addClass('is--hidden'); criterionIndex (int): The index of the criterion, starting from 0.
item (OpenAssessment.RubricOption): The option item to remove.
**/
removeOption: function(criterionIndex, item) {
var criterionItem = this.getCriterionItem(criterionIndex);
criterionItem.optionContainer.remove(item);
}, },
/** /**
Displays the alert. Retrieve all options for a particular criterion.
*/
show : function () { Args:
this.element.removeClass('is--hidden'); criterionIndex (int): The index of the criterion, starting from 0.
Returns:
Array of OpenAssessment.RubricOption
**/
getAllOptions: function(criterionIndex) {
var criterionItem = this.getCriterionItem(criterionIndex);
return criterionItem.optionContainer.getAllItems();
}, },
/** /**
Sets the message of the alert. Retrieve a particular option from the rubric.
How will this work with internationalization?
Args:
Args: criterionIndex (int): The index of the criterion, starting from 0.
newTitle (str): the new title that the message will have optionIndex (int): The index of the option within the criterion,
newMessage (str): the new text that the message's body will contain starting from 0.
*/
setMessage: function (newTitle, newMessage){ Returns:
this.title.text(newTitle); OpenAssessment.RubricOption
this.message.text(newMessage);
**/
getOptionItem: function(criterionIndex, optionIndex) {
var criterionItem = this.getCriterionItem(criterionIndex);
return criterionItem.optionContainer.getItem(optionIndex);
} }
}; };
...@@ -3,7 +3,7 @@ Editing interface for OpenAssessment settings (including assessments). ...@@ -3,7 +3,7 @@ Editing interface for OpenAssessment settings (including assessments).
Args: Args:
element (DOM element): The DOM element representing this view. element (DOM element): The DOM element representing this view.
assessmentViews (array): List of assessment view objects. assessmentViews (object literal): Mapping of CSS IDs to view objects.
Returns: Returns:
OpenAssessment.EditSettingsView OpenAssessment.EditSettingsView
......
/**
A class which controls the validation alert which we place at the top of the rubric page after
changes are made which will propagate to the settings section.
Args:
element (element): The element that specifies the div that the validation consists of.
Returns:
Openassessment.ValidationAlert
*/
OpenAssessment.ValidationAlert = function (element) {
this.element = element;
this.title = $(".openassessment_alert_title", this.element);
this.message = $(".openassessment_alert_message", this.element);
};
OpenAssessment.ValidationAlert.prototype = {
/**
Hides the alert.
*/
hide: function() {
this.element.addClass('is--hidden');
},
/**
Displays the alert.
*/
show : function() {
this.element.removeClass('is--hidden');
},
/**
Sets the message of the alert.
How will this work with internationalization?
Args:
newTitle (str): the new title that the message will have
newMessage (str): the new text that the message's body will contain
*/
setMessage: function(newTitle, newMessage) {
this.title.text(newTitle);
this.message.text(newMessage);
},
/**
Check whether the alert is currently visible.
Returns:
boolean
**/
isVisible: function() {
return !this.element.hasClass('is--hidden');
},
/**
Retrieve the title of the alert.
Returns:
string
**/
getTitle: function() {
return this.title.text();
},
/**
Retrieve the message of the alert.
Returns:
string
**/
getMessage: function() {
return this.message.text();
}
};
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