Merge branch 'MDL-42404_master' of git://github.com/dmonllao/moodle

This commit is contained in:
Sam Hemelryk 2014-02-17 10:15:36 +13:00
commit 487f74813e
5 changed files with 884 additions and 2 deletions

View File

@ -0,0 +1,490 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Steps definitions for rubrics.
*
* @package gradingform_rubric
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php');
use Behat\Gherkin\Node\TableNode as TableNode,
Behat\Behat\Context\Step\Given as Given,
Behat\Behat\Context\Step\When as When,
Behat\Behat\Context\Step\Then as Then,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
Behat\Mink\Exception\ExpectationException as ExpectationException;
/**
* Steps definitions to help with rubrics.
*
* @package gradingform_rubric
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_gradingform_rubric extends behat_base {
/**
* @var The number of levels added by default when a rubric is created.
*/
const DEFAULT_RUBRIC_LEVELS = 3;
/**
* Defines the rubric with the provided data, following rubric's definition grid cells.
*
* This method fills the rubric of the rubric definition
* form; the provided TableNode should contain one row for
* each criterion and each cell of the row should contain:
* # Criterion description
* # Criterion level 1 name
* # Criterion level 1 points
* # Criterion level 2 name
* # Criterion level 2 points
* # Criterion level 3 .....
*
* Works with both JS and non-JS.
*
* @When /^I define the following rubric:$/
* @throws ExpectationException
* @param TableNode $rubric
*/
public function i_define_the_following_rubric(TableNode $rubric) {
// Being a smart method is nothing good when we talk about step definitions, in
// this case we didn't have any other options as there are no labels no elements
// id we can point to without having to "calculate" them.
$steptableinfo = '| criterion description | level1 name | level1 points | level2 name | level2 points | ...';
$criteria = $rubric->getRows();
$addcriterionbutton = $this->find_button(get_string('addcriterion', 'gradingform_rubric'));
// Cleaning the current ones.
$deletebuttons = $this->find_all('css', "input[title='" . get_string('criteriondelete', 'gradingform_rubric') . "']");
if ($deletebuttons) {
// We should reverse the deletebuttons because otherwise once we delete
// the first one the DOM will change and the [X] one will not exist anymore.
$deletebuttons = array_reverse($deletebuttons, true);
foreach ($deletebuttons as $button) {
$this->click_and_confirm($button);
}
}
// The level number (NEWID$N) is not reset after each criterion.
$levelnumber = 1;
// The next criterion is created with the same number of levels than the last criterion.
$defaultnumberoflevels = self::DEFAULT_RUBRIC_LEVELS;
if ($criteria) {
foreach ($criteria as $criterionit => $criterion) {
// Checking the number of cells.
if (count($criterion) % 2 === 0) {
throw new ExpectationException(
'The criterion levels should contain both definition and points, follow this format:' . $steptableinfo,
$this->getSession()
);
}
// Minimum 2 levels per criterion.
// description + definition1 + score1 + definition2 + score2 = 5.
if (count($criterion) < 5) {
throw new ExpectationException(
get_string('err_mintwolevels', 'gradingform_rubric'),
$this->getSession()
);
}
// Add new criterion.
$addcriterionbutton->click();
$criterionroot = 'rubric[criteria][NEWID' . ($criterionit + 1) . ']';
// Getting the criterion description, this one is visible by default.
$this->set_rubric_field_value($criterionroot . '[description]', array_shift($criterion), true);
// When JS is disabled each criterion's levels name numbers starts from 0.
if (!$this->running_javascript()) {
$levelnumber = 0;
}
// Setting the correct number of levels.
$nlevels = count($criterion) / 2;
if ($nlevels < $defaultnumberoflevels) {
// Removing levels if there are too much levels.
// When we add a new level the NEWID$N is increased from the last criterion.
$lastcriteriondefaultlevel = $defaultnumberoflevels + $levelnumber - 1;
$lastcriterionlevel = $nlevels + $levelnumber - 1;
for ($i = $lastcriteriondefaultlevel; $i > $lastcriterionlevel; $i--) {
// If JS is disabled seems that new levels are not added.
if ($this->running_javascript()) {
$deletelevel = $this->find_button($criterionroot . '[levels][NEWID' . $i . '][delete]');
$this->click_and_confirm($deletelevel);
} else {
// Only if the level exists.
$buttonname = $criterionroot . '[levels][NEWID' . $i . '][delete]';
if ($deletelevel = $this->getSession()->getPage()->findButton($buttonname)) {
$deletelevel->click();
}
}
}
} else if ($nlevels > $defaultnumberoflevels) {
// Adding levels if we don't have enough.
$addlevel = $this->find_button($criterionroot . '[levels][addlevel]');
for ($i = ($defaultnumberoflevels + 1); $i <= $nlevels; $i++) {
$addlevel->click();
}
}
// Updating it.
if ($nlevels > self::DEFAULT_RUBRIC_LEVELS) {
$defaultnumberoflevels = $nlevels;
} else {
// If it is less than the default value it sets it to
// the default value.
$defaultnumberoflevels = self::DEFAULT_RUBRIC_LEVELS;
}
foreach ($criterion as $i => $value) {
$levelroot = $criterionroot . '[levels][NEWID' . $levelnumber . ']';
if ($i % 2 === 0) {
// Pairs are the definitions.
$fieldname = $levelroot . '[definition]';
$this->set_rubric_field_value($fieldname, $value);
} else {
// Odds are the points.
// Checking it now, we would need to remove it if we are testing the form validations...
if (!is_numeric($value)) {
throw new ExpectationException(
'The points cells should contain numeric values, follow this format: ' . $steptableinfo,
$this->getSession()
);
}
$fieldname = $levelroot . '[score]';
$this->set_rubric_field_value($fieldname, $value, true);
// Increase the level by one every 2 cells.
$levelnumber++;
}
}
}
}
}
/**
* Replaces a value from the specified criterion. You can use it when editing rubrics, to set both name or points.
*
* @When /^I replace "(?P<current_value_string>(?:[^"]|\\")*)" rubric level with "(?P<value_string>(?:[^"]|\\")*)" in "(?P<criterion_string>(?:[^"]|\\")*)" criterion$/
* @throws ElementNotFoundException
* @param string $currentvalue
* @param string $value
* @param string $criterionname
* @return Given[]
*/
public function i_replace_rubric_level_with($currentvalue, $value, $criterionname) {
$currentvalueliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($currentvalue);
$criterionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($criterionname);
$criterionxpath = "//div[@id='rubric-rubric']" .
"/descendant::td[contains(concat(' ', normalize-space(@class), ' '), ' description ')]";
// It differs between JS on/off.
if ($this->running_javascript()) {
$criterionxpath .= "/descendant::span[@class='textvalue'][text()=$criterionliteral]" .
"/ancestor::tr[contains(concat(' ', normalize-space(@class), ' '), ' criterion ')]";
} else {
$criterionxpath .= "/descendant::textarea[text()=$criterionliteral]" .
"/ancestor::tr[contains(concat(' ', normalize-space(@class), ' '), ' criterion ')]";
}
$inputxpath = $criterionxpath .
"/descendant::input[@type='text'][@value=$currentvalueliteral]";
$textareaxpath = $criterionxpath .
"/descendant::textarea[text()=$currentvalueliteral]";
if ($this->running_javascript()) {
$spansufix = "/ancestor::div[@class='level-wrapper']" .
"/descendant::div[@class='definition']" .
"/descendant::span[@class='textvalue']";
// Expanding the level input boxes.
$spannode = $this->find('xpath', $inputxpath . $spansufix . '|' . $textareaxpath . $spansufix);
$spannode->click();
$inputfield = $this->find('xpath', $inputxpath . '|' . $textareaxpath);
$inputfield->setValue($value);
} else {
$fieldnode = $this->find('xpath', $inputxpath . '|' . $textareaxpath);
$this->set_rubric_field_value($fieldnode->getAttribute('name'), $value);
}
}
/**
* Grades filling the current page rubric. Set one line per criterion and for each criterion set "| Criterion name | Points | Remark |".
*
* @When /^I grade by filling the rubric with:$/
*
* @throws ExpectationException
* @param TableNode $rubric
* @return void
*/
public function i_grade_by_filling_the_rubric_with(TableNode $rubric) {
$criteria = $rubric->getRowsHash();
$stepusage = '"I grade by filling the rubric with:" step needs you to provide a table where each row is a criterion' .
' and each criterion has 3 different values: | Criterion name | Number of points | Remark text |';
// To fill with the steps to execute.
$steps = array();
// First element -> name, second -> points, third -> Remark.
foreach ($criteria as $name => $criterion) {
// We only expect the points and the remark, as the criterion name is $name.
if (count($criterion) !== 2) {
throw new ExpectationException($stepusage, $this->getSession());
}
// Numeric value here.
$points = $criterion[0];
if (!is_numeric($points)) {
throw new ExpectationException($stepusage, $this->getSession());
}
// Selecting a value.
// When JS is disabled there are radio options, with JS enabled divs.
$selectedlevelxpath = $this->get_level_xpath($points);
if ($this->running_javascript()) {
// Only clicking on the selected level if it was not already selected.
$levelnode = $this->find('xpath', $selectedlevelxpath);
// Using in_array() as there are only a few elements.
if (!in_array('checked', explode(' ', $levelnode->getAttribute('class')))) {
$steps[] = new Given('I click on "' . $selectedlevelxpath . '" "xpath_element" in the "' .
$this->escape($name) . '" "table_row"');
}
} else {
// Getting the name of the field.
$radioxpath = $this->get_criterion_xpath($name) .
$selectedlevelxpath . "/descendant::input[@type='radio']";
$radionode = $this->find('xpath', $radioxpath);
// TODO MDL-43738: Change setValue() to use the generic set_value()
// which will delegate the process to the field type.
$radionode->setValue($radionode->getAttribute('value'));
}
// Setting the remark.
// First we need to get the textarea name, then we can set the value.
$textarea = $this->get_node_in_container('css_element', 'textarea', 'table_row', $name);
$steps[] = new Given('I fill in "' . $textarea->getAttribute('name') . '" with "' . $criterion[1] . '"');
}
return $steps;
}
/**
* Checks that the level was previously selected and the user changed to another level.
*
* @Then /^the level with "(?P<points_number>\d+)" points was previously selected for the rubric criterion "(?P<criterion_name_string>(?:[^"]|\\")*)"$/
* @throws ExpectationException
* @param string $criterionname
* @param int $points
* @return void
*/
public function the_level_with_points_was_previously_selected_for_the_rubric_criterion($points, $criterionname) {
$levelxpath = $this->get_criterion_xpath($criterionname) .
$this->get_level_xpath($points) .
"[contains(concat(' ', normalize-space(@class), ' '), ' currentchecked ')]";
// Works both for JS and non-JS.
// - JS: Class -> checked is there when is marked as green.
// - Non-JS: When editing a rubric definition, there are radio inputs and when viewing a
// grade @class contains checked.
$levelxpath .= "[not(contains(concat(' ', normalize-space(@class), ' '), ' checked '))]" .
"[not(/descendant::input[@type='radio'][@checked!='checked'])]";
try {
$this->find('xpath', $levelxpath);
} catch (ElementNotFoundException $e) {
throw new ExpectationException('"' . $points . '" points level was not previously selected', $this->getSession());
}
}
/**
* Checks that the level is currently selected. Works both when grading rubrics and viewing graded rubrics.
*
* @Then /^the level with "(?P<points_number>\d+)" points is selected for the rubric criterion "(?P<criterion_name_string>(?:[^"]|\\")*)"$/
* @throws ExpectationException
* @param string $criterionname
* @param int $points
* @return void
*/
public function the_level_with_points_is_selected_for_the_rubric_criterion($points, $criterionname) {
$levelxpath = $this->get_criterion_xpath($criterionname) .
$this->get_level_xpath($points);
// Works both for JS and non-JS.
// - JS: Class -> checked is there when is marked as green.
// - Non-JS: When editing a rubric definition, there are radio inputs and when viewing a
// grade @class contains checked.
$levelxpath .= "[" .
"contains(concat(' ', normalize-space(@class), ' '), ' checked ')" .
" or " .
"/descendant::input[@type='radio'][@checked='checked']" .
"]";
try {
$this->find('xpath', $levelxpath);
} catch (ElementNotFoundException $e) {
throw new ExpectationException('"' . $points . '" points level is not selected', $this->getSession());
}
}
/**
* Checks that the level is not currently selected. Works both when grading rubrics and viewing graded rubrics.
*
* @Then /^the level with "(?P<points_number>\d+)" points is not selected for the rubric criterion "(?P<criterion_name_string>(?:[^"]|\\")*)"$/
* @throws ExpectationException
* @param string $criterionname
* @param int $points
* @return void
*/
public function the_level_with_points_is_not_selected_for_the_rubric_criterion($points, $criterionname) {
$levelxpath = $this->get_criterion_xpath($criterionname) .
$this->get_level_xpath($points);
// Works both for JS and non-JS.
// - JS: Class -> checked is there when is marked as green.
// - Non-JS: When editing a rubric definition, there are radio inputs and when viewing a
// grade @class contains checked.
$levelxpath .= "[not(contains(concat(' ', normalize-space(@class), ' '), ' checked '))]" .
"[./descendant::input[@type='radio'][@checked!='checked'] or not(./descendant::input[@type='radio'])]";
try {
$this->find('xpath', $levelxpath);
} catch (ElementNotFoundException $e) {
throw new ExpectationException('"' . $points . '" points level is selected', $this->getSession());
}
}
/**
* Makes a hidden rubric field visible (if necessary) and sets a value on it.
*
* @param string $name The name of the field
* @param string $value The value to set
* @param bool $visible
* @return void
*/
protected function set_rubric_field_value($name, $value, $visible = false) {
// Fields are hidden by default.
if ($this->running_javascript() == true && $visible === false) {
$xpath = "//*[@name='$name']/following-sibling::*[contains(concat(' ', normalize-space(@class), ' '), ' plainvalue ')]";
$textnode = $this->find('xpath', $xpath);
$textnode->click();
}
// Set the value now.
$description = $this->find_field($name);
$description->setValue($value);
}
/**
* Performs click confirming the action.
*
* @param NodeElement $node
* @return void
*/
protected function click_and_confirm($node) {
// Clicks to perform the action.
$node->click();
// Confirms the delete.
if ($this->running_javascript()) {
$confirmbutton = $this->get_node_in_container(
'button',
get_string('yes'),
'dialogue',
get_string('confirmation', 'admin')
);
$confirmbutton->click();
}
}
/**
* Returns the xpath representing a selected level.
*
* It is not including the path to the criterion.
*
* It is the xpath when grading a rubric or viewing a rubric,
* it is not the same xpath when editing a rubric.
*
* @param int $points
* @return string
*/
protected function get_level_xpath($points) {
return "//td[contains(concat(' ', normalize-space(@class), ' '), ' level ')]" .
"[./descendant::span[@class='scorevalue'][text()='$points']]";
}
/**
* Returns the xpath representing the selected criterion.
*
* It is the xpath when grading a rubric or viewing a rubric,
* it is not the same xpath when editing a rubric.
*
* @param string $criterionname Literal including the criterion name.
* @return string
*/
protected function get_criterion_xpath($criterionname) {
$literal = $this->getSession()->getSelectorsHandler()->xpathLiteral($criterionname);
return "//tr[contains(concat(' ', normalize-space(@class), ' '), ' criterion ')]" .
"[./descendant::td[@class='description'][text()=$literal]]";
}
}

View File

@ -0,0 +1,159 @@
@gradingform @gradingform_rubric
Feature: Rubrics can be created and edited
In order to use and refine rubrics to grade students
As a teacher
I need to edit previously used rubrics
Background:
Given the following "users" exists:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@asd.com |
| student1 | Student | 1 | student1@asd.com |
And the following "courses" exists:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exists:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I add a "Assignment" to section "1" and I fill the form with:
| Assignment name | Test assignment 1 name |
| Description | Test assignment description |
| Grading method | Rubric |
When I go to "Test assignment 1 name" advanced grading definition page
# Defining a rubric.
And I fill the moodle form with:
| Name | Assignment 1 rubric |
| Description | Rubric test description |
And I define the following rubric:
| TMP Criterion 1 | TMP Level 11 | 11 | TMP Level 12 | 12 |
| TMP Criterion 2 | TMP Level 21 | 21 | TMP Level 22 | 22 |
| TMP Criterion 3 | TMP Level 31 | 31 | TMP Level 32 | 32 |
| TMP Criterion 4 | TMP Level 41 | 41 | TMP Level 42 | 42 |
# Checking that only the last ones are saved.
And I define the following rubric:
| Criterion 1 | Level 11 | 1 | Level 12 | 20 | Level 13 | 40 | Level 14 | 50 |
| Criterion 2 | Level 21 | 10 | Level 22 | 20 | Level 23 | 30 |
| Criterion 3 | Level 31 | 5 | Level 32 | 20 |
And I press "Save as draft"
And I go to "Test assignment 1 name" advanced grading definition page
And I click on "Move down" "button" in the "Criterion 1" "table_row"
And I press "Save rubric and make it ready"
Then I should see "Ready for use"
# Grading two students.
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
And I grade by filling the rubric with:
| Criterion 1 | 50 | Very good |
And I press "Save changes"
# Checking that it complains if you don't select a level for each criterion.
And I should see "Please choose something for each criterion"
And I grade by filling the rubric with:
| Criterion 1 | 50 | Very good |
| Criterion 2 | 10 | Mmmm, you can do it better |
| Criterion 3 | 5 | Not good |
And I complete the advanced grading form with these values:
| Feedback comments | In general... work harder... |
# Checking that the user grade is correct.
And I should see "58.33" in the "Student 1" "table_row"
# Updating the user grade.
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
And I grade by filling the rubric with:
| Criterion 1 | 20 | Bad, I changed my mind |
| Criterion 2 | 10 | Mmmm, you can do it better |
| Criterion 3 | 5 | Not good |
#And the level with "50" points was previously selected for the rubric criterion "Criterion 1"
#And the level with "20" points is selected for the rubric criterion "Criterion 1"
And I save the advanced grading form
And I should see "22.62" in the "Student 1" "table_row"
And I log out
# Viewing it as a student.
And I log in as "student1"
And I follow "Course 1"
And I follow "Test assignment 1 name"
And I should see "22.62" in the ".feedback" "css_element"
And I should see "Rubric test description" in the ".feedback" "css_element"
And I should see "In general... work harder..."
And the level with "10" points is selected for the rubric criterion "Criterion 2"
And the level with "20" points is selected for the rubric criterion "Criterion 1"
And the level with "5" points is selected for the rubric criterion "Criterion 3"
And I log out
And I log in as "teacher1"
And I follow "Course 1"
# Editing a rubric definition without regrading students.
And I go to "Test assignment 1 name" advanced grading definition page
And "Save as draft" "button" should not exists
And I click on "Move up" "button" in the "Criterion 1" "table_row"
And I replace "Level 11" rubric level with "Level 11 edited" in "Criterion 1" criterion
And I press "Save"
And I should see "You are about to save changes to a rubric that has already been used for grading."
And I select "Do not mark for regrade" from "menurubricregrade"
And I press "Continue"
And I log out
# Check that the student still sees the grade.
And I log in as "student1"
And I follow "Course 1"
And I follow "Test assignment 1 name"
And I should see "22.62" in the ".feedback" "css_element"
And the level with "20" points is selected for the rubric criterion "Criterion 1"
And I log out
# Editing a rubric with significant changes.
And I log in as "teacher1"
And I follow "Course 1"
And I go to "Test assignment 1 name" advanced grading definition page
And I click on "Move down" "button" in the "Criterion 2" "table_row"
And I replace "1" rubric level with "11" in "Criterion 1" criterion
And I press "Save"
And I should see "You are about to save significant changes to a rubric that has already been used for grading. The gradebook value will be unchanged, but the rubric will be hidden from students until their item is regraded."
And I press "Continue"
And I log out
# Check that the student doesn't see the grade.
And I log in as "student1"
And I follow "Course 1"
And I follow "Test assignment 1 name"
And I should see "22.62" in the ".feedback" "css_element"
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
And I log out
# Regrade student.
And I log in as "teacher1"
And I follow "Course 1"
And I follow "Test assignment 1 name"
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
And I should see "The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade."
And I save the advanced grading form
And I log out
# Check that the student sees the grade again.
And I log in as "student1"
And I follow "Course 1"
And I follow "Test assignment 1 name"
And I should see "12.16" in the ".feedback" "css_element"
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
# Hide all rubric info for students
And I log out
And I log in as "teacher1"
And I follow "Course 1"
And I go to "Test assignment 1 name" advanced grading definition page
And I uncheck "Allow users to preview rubric used in the module (otherwise rubric will only become visible after grading)"
And I uncheck "Display rubric description during evaluation"
And I uncheck "Display rubric description to those being graded"
And I uncheck "Display points for each level during evaluation"
And I uncheck "Display points for each level to those being graded"
And I press "Save"
And I select "Do not mark for regrade" from "menurubricregrade"
And I press "Continue"
And I log out
# Students should not see anything.
And I log in as "student1"
And I follow "Course 1"
And I follow "Test assignment 1 name"
And I should not see "Criterion 1" in the ".submissionstatustable" "css_element"
And I should not see "Criterion 2" in the ".submissionstatustable" "css_element"
And I should not see "Criterion 3" in the ".submissionstatustable" "css_element"
And I should not see "Rubric test description" in the ".feedback" "css_element"
@javascript
Scenario: I can use rubrics to grade and edit them later updating students grades with Javascript enabled
Scenario: I can use rubrics to grade and edit them later updating students grades with Javascript disabled

View File

@ -0,0 +1,52 @@
@gradingform @gradingform_rubric
Feature: Reuse rubrics in other activities
In order to save time creating duplicated grading forms
As a teacher
I need to reuse rubrics that I created previously
Background:
Given the following "users" exists:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@asd.com |
And the following "courses" exists:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exists:
| user | course | role |
| teacher1 | C1 | editingteacher |
And I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I add a "Assignment" to section "1" and I fill the form with:
| Assignment name | Test assignment 1 name |
| Description | Test assignment 1 description |
| Grading method | Rubric |
And I go to "Test assignment 1 name" advanced grading definition page
And I fill the moodle form with:
| Name | Assignment 1 rubric |
| Description | Assignment 1 description |
And I define the following rubric:
| Criterion 1 | Level 11 | 11 | Level 12 | 12 | Level 3 | 13 |
| Criterion 2 | Level 21 | 21 | Level 22 | 22 | Level 3 | 23 |
| Criterion 3 | Level 31 | 31 | Level 32 | 32 |
And I press "Save rubric and make it ready"
And I follow "Course 1"
When I add a "Assignment" to section "1" and I fill the form with:
| Assignment name | Test assignment 2 name |
| Description | Test assignment 2 description |
| Grading method | Rubric |
And I set "Test assignment 2 name" activity to use "Assignment 1 rubric" grading form
Then I should see "Ready for use"
And I should see "Criterion 1"
And I should see "Criterion 2"
And I should see "Criterion 3"
And I go to "Test assignment 1 name" advanced grading definition page
And I should see "Criterion 1"
And I should see "Criterion 2"
And I should see "Criterion 3"
And I press "Cancel"
@javascript
Scenario: A teacher can reuse one of his/her previously created rubrics, with Javascript enabled
Scenario: A teacher can reuse one of his/her previously created rubrics, with Javascript disabled

View File

@ -0,0 +1,181 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Grading methods steps definitions.
*
* @package core_grading
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
use Behat\Gherkin\Node\TableNode as TableNode,
Behat\Behat\Context\Step\Given as Given,
Behat\Behat\Context\Step\When as When;
/**
* Generic grading methods step definitions.
*
* @package core_grading
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_grading extends behat_base {
/**
* Goes to the selected advanced grading page. You should be in the course page when this step begins.
*
* @Given /^I go to "(?P<activity_name_string>(?:[^"]|\\")*)" advanced grading page$/
* @param string $activityname
* @return Given[]
*/
public function i_go_to_advanced_grading_page($activityname) {
return array(
new Given('I follow "' . $this->escape($activityname) . '"'),
new Given('I follow "' . get_string('gradingmanagement', 'grading') . '"'),
);
}
/**
* Goes to the selected advanced grading definition page. You should be in the course page when this step begins.
*
* @Given /^I go to "(?P<activity_name_string>(?:[^"]|\\")*)" advanced grading definition page$/
* @param string $activityname
* @return Given[]
*/
public function i_go_to_advanced_grading_definition_page($activityname) {
// Transforming to literals, probably not necessary, just in case.
$newactionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string("manageactionnew", "grading"));
$editactionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string("manageactionedit", "grading"));
// Working both when adding and editing.
$definitionxpath = "//a[@class='action']" .
"[./descendant::*[contains(., $newactionliteral) or contains(., $editactionliteral)]]";
return array(
new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
new Given('I click on "' . $this->escape($definitionxpath) . '" "xpath_element"'),
);
}
/**
* Goes to the student's advanced grading page.
*
* @Given /^I go to "(?P<user_fullname_string>(?:[^"]|\\")*)" "(?P<activity_name_string>(?:[^"]|\\")*)" activity advanced grading page$/
* @param string $userfullname The user full name including firstname and lastname.
* @param string $activityname The activity name
* @return Given[]
*/
public function i_go_to_activity_advanced_grading_page($userfullname, $activityname) {
// Step to access the user grade page from the grading page.
$usergradetext = get_string('gradeuser', 'assign', $userfullname);
$gradeuserstep = new Given('I follow "' . $this->escape($usergradetext) . '"');
// Shortcut in case we already are in the grading page.
$usergradetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($usergradetext);
if ($this->getSession()->getPage()->find('named', array('link', $usergradetextliteral))) {
return $gradeuserstep;
}
return array(
new Given('I follow "' . $this->escape($activityname) . '"'),
new Given('I follow "' . $this->escape(get_string('viewgrading', 'assign')) . '"'),
$gradeuserstep
);
}
/**
* Publishes current activity grading defined form as a public template.
*
* @Given /^I publish "(?P<activity_name_string>(?:[^"]|\\")*)" grading form definition as a public template$/
* @param string $activityname
* @return Given[]
*/
public function i_publish_grading_form_definition_as_a_public_template($activityname) {
return array(
new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
new Given('I click on "' . $this->escape(get_string("manageactionshare", "grading")) . '" "link"'),
new Given('I press "' . get_string('continue') . '"')
);
}
/**
* Sets a previously created grading form as the activity grading form.
*
* @Given /^I set "(?P<activity_name_string>(?:[^"]|\\")*)" activity to use "(?P<grading_form_template_string>(?:[^"]|\\")*)" grading form$/
* @param string $activityname
* @param string $templatename
* @return Given[]
*/
public function i_set_activity_to_use_grading_form($activityname, $templatename) {
$templateliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($templatename);
$templatexpath = "//h2[@class='template-name'][contains(., $templateliteral)]/" .
"following-sibling::div[contains(concat(' ', normalize-space(@class), ' '), ' template-actions ')]";
// Should work with both templates and own forms.
$literaltemplate = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('templatepick', 'grading'));
$literalownform = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('templatepickownform', 'grading'));
$usetemplatexpath = "//a[./descendant::div[text()=$literaltemplate]]|" .
"//a[./descendant::div[text()=$literalownform]]";
return array(
new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
new Given('I follow "' . $this->escape(get_string('manageactionclone', 'grading')) . '"'),
new Given('I check "' . get_string('searchownforms', 'grading') . '"'),
new Given('I click on "' . get_string('search') . '" "button" in the "region-main" "region"'),
new Given('I click on "' . $this->escape($usetemplatexpath) . '" "xpath_element" ' .
'in the "' . $this->escape($templatexpath) . '" "xpath_element"'),
new Given('I press "' . get_string('continue') . '"')
);
}
/**
* Saves the current page advanced grading form.
*
* @When /^I save the advanced grading form$/
* @return When[]
*/
public function i_save_the_advanced_grading_form() {
return array(
new When('I press "' . get_string('savechanges') . '"'),
new When('I press "' . get_string('continue') . '"')
);
}
/**
* Grades an activity using advanced grading. Note the grade is set by other steps, depending on the grading method.
*
* @Given /^I complete the advanced grading form with these values:$/
* @param TableNode $data
* @return Given[]
*/
public function i_complete_the_advanced_grading_form_with_these_values(TableNode $data) {
return array(
new Given('I fill the moodle form with:', $data),
new Given('I save the advanced grading form')
);
}
}

View File

@ -83,10 +83,10 @@ class behat_selectors {
*/
protected static $moodleselectors = array(
'dialogue' => <<<XPATH
.//div[contains(concat(' ', normalize-space(@class), ' '), ' moodle-dialogue ')]/descendant::h1[normalize-space(.) = %locator%]/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' moodle-dialogue ')]
//div[contains(concat(' ', normalize-space(@class), ' '), ' moodle-dialogue ')]/descendant::h1[normalize-space(.) = %locator%]/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' moodle-dialogue ')] | //div[contains(concat(' ', normalize-space(@class), ' '), ' yui-dialog ')]/descendant::div[@class='hd'][normalize-space(.) = %locator%]/parent::div
XPATH
, 'block' => <<<XPATH
.//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', %locator%, ' '))] | .//div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]/descendant::h2[normalize-space(.) = %locator%]/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]
//div[contains(concat(' ', normalize-space(@class), ' '), concat(' ', %locator%, ' '))] | //div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]/descendant::h2[normalize-space(.) = %locator%]/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]
XPATH
, 'region' => <<<XPATH
.//*[self::div | self::section | self::aside][./@id = %locator%]