Merge branch 'MDL-81888-main-v02' of https://github.com/ferranrecio/moodle

This commit is contained in:
Huong Nguyen 2024-07-25 08:58:19 +07:00
commit 854296c210
No known key found for this signature in database
GPG Key ID: 40D88AB693A3E72A
13 changed files with 188 additions and 18 deletions

View File

@ -43,6 +43,13 @@ class featureimport extends moodleform {
['accepted_types' => ['.feature']]
);
$mform->addRule('featurefile', null, 'required');
$options = [
0 => get_string('execute_scenarios', 'tool_generator'),
1 => get_string('execute_cleanup', 'tool_generator'),
];
$mform->addElement('select', 'executecleanup', get_string('execute', 'tool_generator'), $options);
$this->add_action_buttons(false, get_string('import'));
}

View File

@ -19,6 +19,8 @@ namespace tool_generator\local\testscenario;
use behat_admin;
use behat_data_generators;
use behat_base;
use behat_course;
use behat_user;
use Behat\Gherkin\Parser;
use Behat\Gherkin\Lexer;
use Behat\Gherkin\Keywords\ArrayKeywords;
@ -49,6 +51,7 @@ class runner {
$this->include_composer_libraries();
$this->include_behat_libraries();
$this->load_generator();
$this->load_cleanup();
}
/**
@ -77,6 +80,9 @@ class runner {
require_once($CFG->libdir . '/behat/behat_base.php');
require_once("{$CFG->libdir}/tests/behat/behat_data_generators.php");
require_once("{$CFG->dirroot}/admin/tests/behat/behat_admin.php");
require_once("{$CFG->dirroot}/course/lib.php");
require_once("{$CFG->dirroot}/course/tests/behat/behat_course.php");
require_once("{$CFG->dirroot}/user/tests/behat/behat_user.php");
return true;
}
@ -97,6 +103,27 @@ class runner {
}
}
/**
* Load all cleanup steps.
*/
private function load_cleanup() {
$extra = $this->scan_method(
new ReflectionMethod(behat_course::class, 'the_course_is_deleted'),
new behat_course(),
);
if ($extra) {
$this->validsteps[$extra->given] = $extra;
}
$extra = $this->scan_method(
new ReflectionMethod(behat_user::class, 'the_user_is_deleted'),
new behat_user(),
);
if ($extra) {
$this->validsteps[$extra->given] = $extra;
}
}
/**
* Scan a generator to get all valid steps.
* @param behat_data_generators $generator the generator to scan.
@ -159,6 +186,19 @@ class runner {
* @return parsedfeature
*/
public function parse_feature(string $content): parsedfeature {
return $this->parse_selected_scenarios($content);
}
/**
* Parse all feature file scenarios.
*
* Note: if no filter is passed, it will execute only the scenarios that are not tagged.
*
* @param string $content the feature file content.
* @param string $filtertag the tag to filter the scenarios.
* @return parsedfeature
*/
private function parse_selected_scenarios(string $content, ?string $filtertag = null): parsedfeature {
$result = new parsedfeature();
$parser = $this->get_parser();
@ -170,6 +210,13 @@ class runner {
if ($feature->hasScenarios()) {
$scenarios = $feature->getScenarios();
foreach ($scenarios as $scenario) {
// By default, we only execute scenaros that are not tagged.
if (empty($filtertag) && !empty($scenario->getTags())) {
continue;
}
if ($filtertag && !in_array($filtertag, $scenario->getTags())) {
continue;
}
if ($scenario->getNodeType() == 'Outline') {
$this->parse_scenario_outline($scenario, $result);
continue;
@ -184,6 +231,15 @@ class runner {
return $result;
}
/**
* Parse a feature file using only the scenarios with cleanup tag.
* @param string $content the feature file content.
* @return parsedfeature
*/
public function parse_cleanup(string $content): parsedfeature {
return $this->parse_selected_scenarios($content, 'cleanup');
}
/**
* Parse a scenario outline.
* @param OutlineNode $scenario the scenario outline to parse.
@ -209,7 +265,6 @@ class runner {
$keywords = new ArrayKeywords([
'en' => [
'feature' => 'Feature',
// If in the future we have clean up steps, background will be renamed to "Clean up".
'background' => 'Background',
'scenario' => 'Scenario',
'scenario_outline' => 'Scenario Outline|Scenario Template',

View File

@ -57,10 +57,12 @@ class parsingresult implements renderable, templatable {
$haslines = false;
foreach ($this->parsedfeature->get_scenarios() as $scenario) {
$scenariodata = [
'type' => $scenario->type,
'name' => $scenario->name,
'type' => ucfirst($scenario->type),
'steps' => [],
];
if (!empty($scenario->name)) {
$scenariodata['name'] = $scenario->name;
}
if (!empty($scenario->error)) {
$scenariodata['scenarioerror'] = $scenario->error;
}

View File

@ -43,10 +43,12 @@ list($options, $unrecognised) = cli_get_params(
'disable-composer' => false,
'composer-upgrade' => true,
'composer-self-update' => true,
'cleanup' => false,
],
[
'h' => 'help',
'f' => 'feature',
'c' => 'cleanup',
]
);
@ -60,11 +62,13 @@ steps.
Usage:
php runtestscenario.php [--feature=\"value\"] [--help]
[--no-composer-self-update] [--no-composer-upgrade]
[--disable-composer]
[--disable-composer] [--cleanup]
Options:
-f, --feature Execute specified feature file (Absolute path of feature file).
-c, --cleanup Execute the scenarios with @cleanup tag.
--no-composer-self-update
Prevent upgrade of the composer utility using its self-update command
@ -79,6 +83,7 @@ Note: Installation of composer and/or dependencies will still happen as required
Example from Moodle root directory:
\$ php admin/tool/generator/cli/runtestscenario.php --feature=/path/to/some/testing/scenario.feature
\$ php admin/tool/generator/cli/runtestscenario.php --feature=/path/to/some/testing/scenario.feature --cleanup
";
if (!empty($options['help'])) {
@ -139,7 +144,11 @@ if (empty($content)) {
}
try {
$parsedfeature = $runner->parse_feature($content);
if (!empty($options['cleanup'])) {
$parsedfeature = $runner->parse_cleanup($content);
} else {
$parsedfeature = $runner->parse_feature($content);
}
} catch (\Exception $error) {
echo "Error parsing feature file: {$error->getMessage()}\n";
echo "Use the web version of the tool to see the parsing details:\n";

View File

@ -64,6 +64,9 @@ $string['error_nonexistingcourse'] = 'The specified course does not exist';
$string['error_nopageinstances'] = 'The selected course does not contain page module instances';
$string['error_notdebugging'] = 'Not available on this server because debugging is not set to DEVELOPER';
$string['error_nouserspassword'] = 'You need to set $CFG->tool_generator_users_password in config.php to generate the test plan';
$string['execute'] = 'Execute';
$string['execute_cleanup'] = 'Cleanup scenarios';
$string['execute_scenarios'] = 'Testing scenarios';
$string['fullname'] = 'Test course: {$a->size}';
$string['maketestcourse'] = 'Make test course';
$string['maketestplan'] = 'Make JMeter test plan';

View File

@ -79,7 +79,11 @@ if (empty($content)) {
}
try {
$parsedfeature = $runner->parse_feature($content);
if ($data->executecleanup) {
$parsedfeature = $runner->parse_cleanup($content);
} else {
$parsedfeature = $runner->parse_feature($content);
}
} catch (\Throwable $th) {
echo $output->notification(get_string('testscenario_errorparsing', 'tool_generator', $th->getMessage()));
echo $output->continue_button($currenturl);

View File

@ -27,7 +27,7 @@
"scenarios": [
{
"type": "Scenario",
"title": "Scenario title",
"name": "Scenario title",
"hassteps": true,
"steps": [
{
@ -53,7 +53,7 @@
<p>{{#str}} testscenario_steps, tool_generator {{/str}}</p>
{{/haslines}}
{{#scenarios}}
<h3>{{type}}: {{name}}</h3>
<h3>{{type}}{{#name}}: {{name}} {{/name}}</h3>
<div>
{{#scenarioerror}}
<div class="alert alert-danger mb-3" role="alert">

View File

@ -74,3 +74,25 @@ Feature: Create testing scenarios using generators
And I should see "Course 2" in the "page-header" "region"
And I am on the "C3" "Course" page
And I should see "Course 3" in the "page-header" "region"
@javascript
Scenario: Run cleanup steps after creating a testing scenario
Given I log in as "admin"
And I navigate to "Development > Create testing scenarios" in site administration
And I upload "admin/tool/generator/tests/fixtures/testscenario/scenario_cleanup.feature" file to "Feature file" filemanager
And I press "Import"
And I should see "Scenario: Create course content to cleanup later"
And I navigate to "Courses > Manage courses and categories" in site administration
And I should see "Course cleanup" in the "course-listing" "region"
And I navigate to "Users > Accounts > Browse list of users" in site administration
And I should see "Teacher Test1"
And I navigate to "Development > Create testing scenarios" in site administration
When I upload "admin/tool/generator/tests/fixtures/testscenario/scenario_cleanup.feature" file to "Feature file" filemanager
And I set the field "Execute" to "Cleanup scenarios"
And I press "Import"
And I should see "the course \"Course cleanup\" is deleted"
And I should see "the user \"cleanteacher\" is deleted"
Then I navigate to "Courses > Manage courses and categories" in site administration
And I should not see "Course cleanup" in the "course-listing" "region"
And I navigate to "Users > Accounts > Browse list of users" in site administration
And I should not see "Teacher Test1"

View File

@ -12,19 +12,33 @@ Feature: Fixture to prepare scenario for testing
| activity | name | intro | course | idnumber | section | visible |
| assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 | 1 |
| assign | Activity sample 2 | Test assignment description | C1 | sample2 | 1 | 0 |
@cleanup
Scenario: clean course from fixture to prepare scenario for testing
Given the course "Course test" is deleted
Scenario: Create users
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | Test1 | sample@example.com |
| username | firstname | lastname | email |
| teachersample | Teacher | Test1 | sample@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teachersample | C1 | editingteacher |
And "5" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | Test[count] |
| email | student[count]@example.com |
| username | studentsample[count] |
| firstname | Student |
| lastname | Test[count] |
| email | studentsample[count]@example.com |
And "5" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role | student |
| user | studentsample[count] |
| course | C1 |
| role | student |
@cleanup
Scenario: clean users from fixture to prepare scenario for testing
Given the user "teachersample" is deleted
And the user "studentsample1" is deleted
And the user "studentsample2" is deleted
And the user "studentsample3" is deleted
And the user "studentsample4" is deleted
And the user "studentsample5" is deleted

View File

@ -0,0 +1,22 @@
Feature: Fixture to test cleanup of a testing scenario
Scenario: Create course content to cleanup later
Given the following config values are set as admin:
| sendcoursewelcomemessage | 0 | enrol_manual |
And the following "course" exists:
| fullname | Course cleanup |
| shortname | Cleanup |
| category | 0 |
| numsections | 3 |
| initsections | 1 |
Given the following "users" exist:
| username | firstname | lastname | email |
| cleanteacher | Teacher | Test1 | samplecleanup@example.com |
And the following "course enrolments" exist:
| user | course | role |
| cleanteacher | Cleanup | editingteacher |
@cleanup
Scenario: remove fixture to test cleanup of a testing scenario
Given the course "Course cleanup" is deleted
And the user "cleanteacher" is deleted

View File

@ -12,3 +12,9 @@ Feature: Fixture to prepare scenario for testing from an outline
| Course 1 | C1 |
| Course 2 | C2 |
| Course 3 | C3 |
@cleanup
Scenario: clean up fixture to prepare scenario for testing from an outline
Given the course "Course 1" is deleted
And the course "Course 2" is deleted
And the course "Course 3" is deleted

View File

@ -1138,6 +1138,17 @@ class behat_course extends behat_base {
return $steps;
}
/**
* Deletes a course.
*
* @Given the course :coursefullname is deleted
* @param string $coursefullname
*/
public function the_course_is_deleted($coursefullname) {
delete_course($this->get_course_id($coursefullname), false);
fix_course_sortorder();
}
/**
* Duplicates the activity or resource specified by it's name. You should be in the course page with editing mode on.
*

View File

@ -52,6 +52,21 @@ class behat_user extends behat_base {
]);
}
/**
* Deletes a user.
*
* @Given the user :identifier is deleted
* @param string $identifier
*/
public function the_user_is_deleted($identifier) {
global $DB;
$userid = $this->get_user_id_by_identifier($identifier);
if (!$userid) {
throw new moodle_exception('The specified user with username or email "' . $identifier . '" does not exist');
}
delete_user($DB->get_record('user', ['id' => $userid]));
}
/**
* The input field should have autocomplete set to this value.
*