MDL-81885 tool_generator: add admin settings generator

The tool_generator creates a testing scenario that can
execute all steps from behat_data_generators (with some
limitations). However, it cannot set any admin settings,
which limits the tool's ability to generate many
real test scenarios.
This commit is contained in:
ferranrecio
2024-06-03 11:53:54 +02:00
parent bcd8e0d6ed
commit 4e9a8c6cd7
6 changed files with 99 additions and 53 deletions
@@ -16,12 +16,15 @@
namespace tool_generator\local\testscenario;
use behat_admin;
use behat_data_generators;
use behat_base;
use Behat\Gherkin\Parser;
use Behat\Gherkin\Lexer;
use Behat\Gherkin\Keywords\ArrayKeywords;
use ReflectionClass;
use ReflectionMethod;
use stdClass;
/**
* Class to process a scenario generator file.
@@ -72,6 +75,7 @@ class runner {
require_once($CFG->libdir . '/behat/classes/behat_command.php');
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");
return true;
}
@@ -81,6 +85,15 @@ class runner {
private function load_generator() {
$this->generator = new behat_data_generators();
$this->validsteps = $this->scan_generator($this->generator);
// Set config values is not inside the general behat generators.
$extra = $this->scan_method(
new ReflectionMethod(behat_admin::class, 'the_following_config_values_are_set_as_admin'),
new behat_admin(),
);
if ($extra) {
$this->validsteps[$extra->given] = $extra;
}
}
/**
@@ -93,14 +106,32 @@ class runner {
$class = new ReflectionClass($generator);
$methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
$given = $this->get_method_given($method);
if ($given) {
$result[$given] = $method->getName();
$scan = $this->scan_method($method, $generator);
if ($scan) {
$result[$scan->given] = $scan;
}
}
return $result;
}
/**
* Scan a method to get the given expression tag.
* @param ReflectionMethod $method the method to scan.
* @param behat_base $behatclass the behat class instance to use.
* @return stdClass|null the method data (given, name, class).
*/
private function scan_method(ReflectionMethod $method, behat_base $behatclass): ?stdClass {
$given = $this->get_method_given($method);
if (!$given) {
return null;
}
return (object)[
'given' => $given,
'name' => $method->getName(),
'generator' => $behatclass,
];
}
/**
* Get the given expression tag of a method.
*
@@ -146,7 +177,7 @@ class runner {
$result->add_scenario($scenario->getNodeType(), $scenario->getTitle());
$steps = $scenario->getSteps();
foreach ($steps as $step) {
$result->add_step(new steprunner($this->generator, $this->validsteps, $step));
$result->add_step(new steprunner(null, $this->validsteps, $step));
}
}
}
@@ -16,7 +16,7 @@
namespace tool_generator\local\testscenario;
use behat_data_generators;
use behat_base;
use Behat\Gherkin\Node\StepNode;
/**
@@ -27,8 +27,8 @@ use Behat\Gherkin\Node\StepNode;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class steprunner {
/** @var behat_data_generators the behat data generator instance. */
private behat_data_generators $generator;
/** @var behat_base|null the behat step class instance. */
private ?behat_base $generator = null;
/** @var array the valid steps indexed by given expression tag. */
private array $validsteps;
@@ -53,12 +53,14 @@ class steprunner {
/**
* Constructor.
* @param behat_data_generators $generator the behat data generator instance.
* @param behat_base|null $unused This does nothing, do not use it.
* @param array $validsteps the valid steps indexed by given expression tag.
* @param StepNode $stepnode the step node to process.
*/
public function __construct(behat_data_generators $generator, array $validsteps, StepNode $stepnode) {
$this->generator = $generator;
public function __construct($unused, array $validsteps, StepNode $stepnode) {
if ($unused !== null) {
debugging('Deprecated argument passed to ' . __FUNCTION__, DEBUG_DEVELOPER);
}
$this->validsteps = $validsteps;
$this->stepnode = $stepnode;
$this->init();
@@ -73,12 +75,13 @@ class steprunner {
private function init() {
$matches = [];
$linetext = $this->stepnode->getText();
foreach ($this->validsteps as $pattern => $method) {
if (!$this->match_given($pattern, $linetext, $matches)) {
foreach ($this->validsteps as $method) {
if (!$this->match_given($method->given, $linetext, $matches)) {
continue;
}
$this->method = $method;
$this->params = $this->build_method_params($method, $matches);
$this->method = $method->name;
$this->params = $this->build_method_params($method->name, $matches, $method->generator);
$this->generator = $method->generator;
$this->isvalid = true;
return;
}
@@ -89,10 +92,11 @@ class steprunner {
* Build the method parameters.
* @param string $methodname the method name.
* @param array $matches the matches.
* @param behat_base $generator the method class.
* @return array the method parameters.
*/
private function build_method_params($methodname, $matches) {
$method = new \ReflectionMethod($this->generator, $methodname);
private function build_method_params(string $methodname, array $matches, behat_base $generator) {
$method = new \ReflectionMethod($generator, $methodname);
$params = [];
foreach ($method->getParameters() as $param) {
$paramname = $param->getName();
@@ -21,6 +21,11 @@ Feature: Create testing scenarios using generators
And I should see "Student Test3"
And I should see "Student Test4"
And I should see "Student Test5"
And I set the field "Participants tertiary navigation" to "Enrolment methods"
And I click on "Edit" "link" in the "Manual enrolments" "table_row"
And the field "Send course welcome message" matches value "No"
And I navigate to "Plugins > Enrolments > Manual enrolments" in site administration
And the field "Send course welcome message" matches value "No"
@javascript
Scenario: Prevent creating a testing scenario with no steps to execute
@@ -1,6 +1,8 @@
Feature: Prepare scenario for testing
Scenario: Create course content
Given the following "course" exists:
Given the following config values are set as admin:
| sendcoursewelcomemessage | 0 | enrol_manual |
And the following "course" exists:
| fullname | Course test |
| shortname | C1 |
| category | 0 |
@@ -46,7 +46,7 @@ class runner_test extends \advanced_testcase {
$feature = $runner->parse_feature($contents);
$this->assertEquals(2, count($feature->get_scenarios()));
$this->assertEquals(6, count($feature->get_all_steps()));
$this->assertEquals(7, count($feature->get_all_steps()));
$this->assertTrue($feature->is_valid());
$result = $runner->execute($feature);
@@ -57,6 +57,31 @@ class steprunner_test extends \advanced_testcase {
return $steps[0];
}
/**
* Get the list of valid behat steps for the tests.
* @return array the valid steps details.
*/
private function get_valid_steps(): array {
$generator = new behat_data_generators();
return [
'/^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/' => (object) [
'name' => 'the_following_entities_exist',
'given' => '/^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/',
'generator' => $generator,
],
':count :entitytype exist with the following data:' => (object) [
'name' => 'the_following_repeated_entities_exist',
'given' => ':count :entitytype exist with the following data:',
'generator' => $generator,
],
'the following :entitytype exists:' => (object) [
'name' => 'the_following_entity_exists',
'given' => 'the following :entitytype exists:',
'generator' => $generator,
],
];
}
/**
* Test for parse_feature.
* @covers ::is_valid
@@ -65,15 +90,9 @@ class steprunner_test extends \advanced_testcase {
* @dataProvider execute_steps_provider
*/
public function test_is_valid(string $step, bool $expected): void {
$generator = new behat_data_generators();
$validsteps = [
'/^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/' => 'the_following_entities_exist',
':count :entitytype exist with the following data:' => 'the_following_repeated_entities_exist',
'the following :entitytype exists:' => 'the_following_entity_exists',
];
$validsteps = $this->get_valid_steps();
$step = $this->get_step($step);
$steprunner = new steprunner($generator, $validsteps, $step);
$steprunner = new steprunner(null, $validsteps, $step);
$this->assertEquals($expected, $steprunner->is_valid());
}
@@ -91,15 +110,10 @@ class steprunner_test extends \advanced_testcase {
$this->resetAfterTest();
$generator = new behat_data_generators();
$validsteps = [
'/^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/' => 'the_following_entities_exist',
':count :entitytype exist with the following data:' => 'the_following_repeated_entities_exist',
'the following :entitytype exists:' => 'the_following_entity_exists',
];
$validsteps = $this->get_valid_steps();
$step = $this->get_step($step);
$steprunner = new steprunner($generator, $validsteps, $step);
$steprunner = new steprunner(null, $validsteps, $step);
$this->assertFalse($steprunner->is_executed());
@@ -162,18 +176,13 @@ class steprunner_test extends \advanced_testcase {
$this->resetAfterTest();
$generator = new behat_data_generators();
$validsteps = [
'/^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/' => 'the_following_entities_exist',
':count :entitytype exist with the following data:' => 'the_following_repeated_entities_exist',
'the following :entitytype exists:' => 'the_following_entity_exists',
];
$validsteps = $this->get_valid_steps();
$step = $this->get_step('Given the following "course" exists:
| fullname | Course test |
| shortname | C1 |
| category | 0 |');
$steprunner = new steprunner($generator, $validsteps, $step);
$steprunner = new steprunner(null, $validsteps, $step);
$this->assertFalse($steprunner->is_executed());
@@ -190,7 +199,7 @@ class steprunner_test extends \advanced_testcase {
);
// Execute the same course creation.
$steprunner = new steprunner($generator, $validsteps, $step);
$steprunner = new steprunner(null, $validsteps, $step);
$this->assertFalse($steprunner->is_executed());
$result = $steprunner->execute();
$this->assertFalse($result);
@@ -204,19 +213,14 @@ class steprunner_test extends \advanced_testcase {
* @covers ::get_arguments_string
*/
public function test_get_step_content(): void {
$generator = new behat_data_generators();
$validsteps = [
'/^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/' => 'the_following_entities_exist',
':count :entitytype exist with the following data:' => 'the_following_repeated_entities_exist',
'the following :entitytype exists:' => 'the_following_entity_exists',
];
$step = $this->get_step('Given the following "course" exists:
| fullname | Course test |
| shortname | C1 |
| category | 0 |
| numsections | 3 |');
$steprunner = new steprunner($generator, $validsteps, $step);
| fullname | Course test |
| shortname | C1 |
| category | 0 |
| numsections | 3 |');
$validsteps = $this->get_valid_steps();
$steprunner = new steprunner(null, $validsteps, $step);
$this->assertEquals(
'the following "course" exists:',