Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace Moodle\BehatExtension\ServiceContainer;
  18  
  19  use Behat\Behat\Definition\ServiceContainer\DefinitionExtension;
  20  use Behat\Behat\EventDispatcher\ServiceContainer\EventDispatcherExtension;
  21  use Behat\Behat\Gherkin\ServiceContainer\GherkinExtension;
  22  use Behat\Behat\Tester\ServiceContainer\TesterExtension;
  23  use Behat\Testwork\Cli\ServiceContainer\CliExtension;
  24  use Behat\Testwork\Output\ServiceContainer\OutputExtension;
  25  use Behat\Testwork\ServiceContainer\Extension as ExtensionInterface;
  26  use Behat\Testwork\ServiceContainer\ExtensionManager;
  27  use Behat\Testwork\ServiceContainer\ServiceProcessor;
  28  use Behat\Testwork\Suite\ServiceContainer\SuiteExtension;
  29  use Moodle\BehatExtension\Driver\WebDriverFactory;
  30  use Moodle\BehatExtension\Output\Formatter\MoodleProgressFormatterFactory;
  31  use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  32  use Symfony\Component\Config\FileLocator;
  33  use Symfony\Component\DependencyInjection\ContainerBuilder;
  34  use Symfony\Component\DependencyInjection\Definition;
  35  use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  36  use Symfony\Component\DependencyInjection\Reference;
  37  
  38  // phpcs:disable moodle.NamingConventions.ValidFunctionName.LowercaseMethod
  39  
  40  /**
  41   * Behat extension for moodle
  42   *
  43   * Provides multiple features directory loading (Gherkin\Loader\MoodleFeaturesSuiteLoader
  44   *
  45   * @package core
  46   * @copyright 2016 Rajesh Taneja <rajesh@moodle.com>
  47   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  48   */
  49  class BehatExtension implements ExtensionInterface {
  50      /** @var string Extension configuration ID */
  51      const MOODLE_ID = 'moodle';
  52  
  53      /** @var ServiceProcessor */
  54      private $processor;
  55  
  56      /**
  57       * Initializes compiler pass.
  58       *
  59       * @param null|ServiceProcessor $processor
  60       */
  61      public function __construct(ServiceProcessor $processor = null) {
  62          $this->processor = $processor ? : new ServiceProcessor();
  63      }
  64  
  65      /**
  66       * Loads moodle specific configuration.
  67       *
  68       * @param ContainerBuilder $container ContainerBuilder instance
  69       * @param array            $config    Extension configuration hash (from behat.yml)
  70       */
  71      public function load(ContainerBuilder $container, array $config) {
  72          $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/services'));
  73          $loader->load('core.xml');
  74  
  75          // Getting the extension parameters.
  76          $container->setParameter('behat.moodle.parameters', $config);
  77  
  78          // Load moodle progress formatter.
  79          $moodleprogressformatter = new MoodleProgressFormatterFactory();
  80          $moodleprogressformatter->buildFormatter($container);
  81  
  82          // Load custom step tester event dispatcher.
  83          $this->loadEventDispatchingStepTester($container);
  84  
  85          // Load chained step tester.
  86          $this->loadChainedStepTester($container);
  87  
  88          // Load step count formatter.
  89          $this->loadMoodleListFormatter($container);
  90  
  91          // Load step count formatter.
  92          $this->loadMoodleStepcountFormatter($container);
  93  
  94          // Load screenshot formatter.
  95          $this->loadMoodleScreenshotFormatter($container);
  96  
  97          // Load namespace alias.
  98          $this->alias_old_namespaces();
  99      }
 100  
 101      /**
 102       * Loads moodle List formatter.
 103       *
 104       * @param ContainerBuilder $container
 105       */
 106      protected function loadMoodleListFormatter(ContainerBuilder $container) {
 107          $definition = new Definition('Moodle\BehatExtension\Output\Formatter\MoodleListFormatter', [
 108              'moodle_list',
 109              'List all scenarios. Use with --dry-run',
 110              ['stepcount' => false],
 111              $this->createOutputPrinterDefinition()
 112          ]);
 113          $definition->addTag(OutputExtension::FORMATTER_TAG, ['priority' => 101]);
 114          $container->setDefinition(OutputExtension::FORMATTER_TAG . '.moodle_list', $definition);
 115      }
 116  
 117      /**
 118       * Loads moodle Step count formatter.
 119       *
 120       * @param ContainerBuilder $container
 121       */
 122      protected function loadMoodleStepcountFormatter(ContainerBuilder $container) {
 123          $definition = new Definition('Moodle\BehatExtension\Output\Formatter\MoodleStepcountFormatter', [
 124              'moodle_stepcount',
 125              'Count steps in feature files. Use with --dry-run',
 126              ['stepcount' => false],
 127              $this->createOutputPrinterDefinition()
 128          ]);
 129          $definition->addTag(OutputExtension::FORMATTER_TAG, ['priority' => 101]);
 130          $container->setDefinition(OutputExtension::FORMATTER_TAG . '.moodle_stepcount', $definition);
 131      }
 132  
 133      /**
 134       * Loads moodle screenshot formatter.
 135       *
 136       * @param ContainerBuilder $container
 137       */
 138      protected function loadMoodleScreenshotFormatter(ContainerBuilder $container) {
 139          $definition = new Definition('Moodle\BehatExtension\Output\Formatter\MoodleScreenshotFormatter', [
 140              'moodle_screenshot',
 141              // phpcs:ignore Generic.Files.LineLength.TooLong
 142              'Take screenshot of all steps. Use --format-settings \'{"formats": "html,image"}\' to get specific o/p type',
 143              ['formats' => 'html,image'],
 144              $this->createOutputPrinterDefinition()
 145          ]);
 146          $definition->addTag(OutputExtension::FORMATTER_TAG, ['priority' => 102]);
 147          $container->setDefinition(OutputExtension::FORMATTER_TAG . '.moodle_screenshot', $definition);
 148      }
 149  
 150      /**
 151       * Creates output printer definition.
 152       *
 153       * @return Definition
 154       */
 155      protected function createOutputPrinterDefinition() {
 156          return new Definition('Behat\Testwork\Output\Printer\StreamOutputPrinter', [
 157              new Definition('Behat\Behat\Output\Printer\ConsoleOutputFactory'),
 158          ]);
 159      }
 160  
 161      /**
 162       * Loads definition printers.
 163       *
 164       * @param ContainerBuilder $container
 165       */
 166      private function loadDefinitionPrinters(ContainerBuilder $container) {
 167          $definition = new Definition('Moodle\BehatExtension\Definition\Printer\ConsoleDefinitionInformationPrinter', [
 168              new Reference(CliExtension::OUTPUT_ID),
 169              new Reference(DefinitionExtension::PATTERN_TRANSFORMER_ID),
 170              new Reference(DefinitionExtension::DEFINITION_TRANSLATOR_ID),
 171              new Reference(GherkinExtension::KEYWORDS_ID)
 172          ]);
 173          $container->removeDefinition('definition.information_printer');
 174          $container->setDefinition('definition.information_printer', $definition);
 175      }
 176  
 177      /**
 178       * Loads definition controller.
 179       *
 180       * @param ContainerBuilder $container
 181       */
 182      private function loadController(ContainerBuilder $container) {
 183          $definition = new Definition('Moodle\BehatExtension\Definition\Cli\AvailableDefinitionsController', [
 184              new Reference(SuiteExtension::REGISTRY_ID),
 185              new Reference(DefinitionExtension::WRITER_ID),
 186              new Reference('definition.list_printer'),
 187              new Reference('definition.information_printer')
 188          ]);
 189          $container->removeDefinition(CliExtension::CONTROLLER_TAG . '.available_definitions');
 190          $container->setDefinition(CliExtension::CONTROLLER_TAG . '.available_definitions', $definition);
 191      }
 192  
 193      /**
 194       * Loads chained step tester.
 195       *
 196       * @param ContainerBuilder $container
 197       */
 198      protected function loadChainedStepTester(ContainerBuilder $container) {
 199          // Chained steps.
 200          $definition = new Definition('Moodle\BehatExtension\EventDispatcher\Tester\ChainedStepTester', [
 201              new Reference(TesterExtension::STEP_TESTER_ID),
 202          ]);
 203          $definition->addTag(TesterExtension::STEP_TESTER_WRAPPER_TAG, ['priority' => 100]);
 204          $container->setDefinition(TesterExtension::STEP_TESTER_WRAPPER_TAG . '.substep', $definition);
 205      }
 206  
 207      /**
 208       * Loads event-dispatching step tester.
 209       *
 210       * @param ContainerBuilder $container
 211       */
 212      protected function loadEventDispatchingStepTester(ContainerBuilder $container) {
 213          $definition = new Definition('Moodle\BehatExtension\EventDispatcher\Tester\MoodleEventDispatchingStepTester', [
 214              new Reference(TesterExtension::STEP_TESTER_ID),
 215              new Reference(EventDispatcherExtension::DISPATCHER_ID)
 216          ]);
 217          $definition->addTag(TesterExtension::STEP_TESTER_WRAPPER_TAG, ['priority' => -9999]);
 218          $container->setDefinition(TesterExtension::STEP_TESTER_WRAPPER_TAG . '.event_dispatching', $definition);
 219      }
 220  
 221      /**
 222       * Setups configuration for current extension.
 223       *
 224       * @param ArrayNodeDefinition $builder
 225       */
 226      public function configure(ArrayNodeDefinition $builder) {
 227          // phpcs:disable PEAR.WhiteSpace.ObjectOperatorIndent.Incorrect
 228          $builder->children()
 229              ->arrayNode('capabilities')
 230                  ->useAttributeAsKey('key')
 231                  ->prototype('variable')->end()
 232                  ->end()
 233              ->arrayNode('steps_definitions')
 234                  ->useAttributeAsKey('key')
 235                  ->prototype('variable')->end()
 236                  ->end()
 237              ->scalarNode('moodledirroot')
 238                  ->defaultNull()
 239                  ->end()
 240              ->end()
 241          ->end();
 242          // phpcs:enable PEAR.WhiteSpace.ObjectOperatorIndent.Incorrect
 243      }
 244  
 245      /**
 246       * Returns the extension config key.
 247       *
 248       * @return string
 249       */
 250      public function getConfigKey() {
 251          return self::MOODLE_ID;
 252      }
 253  
 254      /**
 255       * Initializes other extensions.
 256       *
 257       * This method is called immediately after all extensions are activated but
 258       * before any extension `configure()` method is called. This allows extensions
 259       * to hook into the configuration of other extensions providing such an
 260       * extension point.
 261       *
 262       * @param ExtensionManager $extensionmanager
 263       */
 264      public function initialize(ExtensionManager $extensionmanager) {
 265          if (null !== $minkextension = $extensionmanager->getExtension('mink')) {
 266              $minkextension->registerDriverFactory(new WebDriverFactory());
 267          }
 268      }
 269  
 270      /**
 271       * You can modify the container here before it is dumped to PHP code.
 272       *
 273       * @param ContainerBuilder $container
 274       */
 275      public function process(ContainerBuilder $container) {
 276          // Load controller for definition printing.
 277          $this->loadDefinitionPrinters($container);
 278          $this->loadController($container);
 279      }
 280  
 281      /**
 282       * Alias old namespace of given. when and then for BC.
 283       */
 284      private function alias_old_namespaces() {
 285          class_alias('Moodle\\BehatExtension\\Context\\Step\\Given', 'Behat\\Behat\\Context\\Step\\Given', true);
 286          class_alias('Moodle\\BehatExtension\\Context\\Step\\When', 'Behat\\Behat\\Context\\Step\\When', true);
 287          class_alias('Moodle\\BehatExtension\\Context\\Step\\Then', 'Behat\\Behat\\Context\\Step\\Then', true);
 288      }
 289  }