See Release Notes
Long Term Support Release
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\EventDispatcher\Tester; 18 19 use Behat\Behat\EventDispatcher\Event\AfterStepSetup; 20 use Behat\Behat\EventDispatcher\Event\AfterStepTested; 21 use Behat\Behat\EventDispatcher\Event\BeforeStepTeardown; 22 use Behat\Behat\EventDispatcher\Event\BeforeStepTested; 23 use Behat\Behat\Tester\Result\ExecutedStepResult; 24 use Behat\Behat\Tester\Result\SkippedStepResult; 25 use Behat\Behat\Tester\Result\StepResult; 26 use Behat\Behat\Tester\Result\UndefinedStepResult; 27 use Behat\Behat\Tester\StepTester; 28 use Behat\Gherkin\Node\FeatureNode; 29 use Behat\Gherkin\Node\StepNode; 30 use Behat\Testwork\Call\CallResult; 31 use Behat\Testwork\Environment\Environment; 32 use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; 33 use Moodle\BehatExtension\Context\Step\ChainedStep; 34 use Moodle\BehatExtension\Exception\SkippedException; 35 use Symfony\Component\EventDispatcher\EventDispatcherInterface; 36 37 // phpcs:disable moodle.NamingConventions.ValidFunctionName.LowercaseMethod 38 39 /** 40 * Override step tester to ensure chained steps gets executed. 41 * 42 * @package core 43 * @copyright 2016 Rajesh Taneja <rajesh@moodle.com> 44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 */ 46 class ChainedStepTester implements StepTester { 47 /** 48 * The text of the step to look for exceptions / debugging messages. 49 */ 50 const EXCEPTIONS_STEP_TEXT = 'I look for exceptions'; 51 52 /** 53 * @var StepTester Base step tester. 54 */ 55 private $singlesteptester; 56 57 /** 58 * @var EventDispatcher keep step event dispatcher. 59 */ 60 private $eventdispatcher; 61 62 /** 63 * Keep status of chained steps if used. 64 * @var bool 65 */ 66 protected static $chainedstepused = false; 67 68 /** 69 * Constructor. 70 * 71 * @param StepTester $steptester single step tester. 72 */ 73 public function __construct(StepTester $steptester) { 74 $this->singlesteptester = $steptester; 75 } 76 77 /** 78 * Set event dispatcher to use for events. 79 * 80 * @param EventDispatcherInterface $eventdispatcher 81 */ 82 public function setEventDispatcher(EventDispatcherInterface $eventdispatcher) { 83 $this->eventdispatcher = $eventdispatcher; 84 } 85 86 /** 87 * Sets up step for a test. 88 * 89 * @param Environment $env 90 * @param FeatureNode $feature 91 * @param StepNode $step 92 * @param bool $skip 93 * 94 * @return Setup 95 */ 96 public function setUp(Environment $env, FeatureNode $feature, StepNode $step, $skip) { 97 return $this->singlesteptester->setUp($env, $feature, $step, $skip); 98 } 99 100 /** 101 * Tests step. 102 * 103 * @param Environment $env 104 * @param FeatureNode $feature 105 * @param StepNode $step 106 * @param bool $skip 107 * @return StepResult 108 */ 109 public function test(Environment $env, FeatureNode $feature, StepNode $step, $skip) { 110 $result = $this->singlesteptester->test($env, $feature, $step, $skip); 111 112 if (!($result instanceof ExecutedStepResult) || !$this->supportsResult($result->getCallResult())) { 113 $result = $this->checkSkipResult($result); 114 115 // If undefined step then don't continue chained steps. 116 if ($result instanceof UndefinedStepResult) { 117 return $result; 118 } 119 120 // If exception caught, then don't continue chained steps. 121 if (($result instanceof ExecutedStepResult) && $result->hasException()) { 122 return $result; 123 } 124 125 // If step is skipped, then return. no need to continue chain steps. 126 if ($result instanceof SkippedStepResult) { 127 return $result; 128 } 129 130 // Check for exceptions. 131 // Extra step, looking for a moodle exception, a debugging() message or a PHP debug message. 132 $checkingstep = new StepNode('Given', self::EXCEPTIONS_STEP_TEXT, [], $step->getLine()); 133 $afterexceptioncheckingevent = $this->singlesteptester->test($env, $feature, $checkingstep, $skip); 134 $exceptioncheckresult = $this->checkSkipResult($afterexceptioncheckingevent); 135 136 if (!$exceptioncheckresult->isPassed()) { 137 return $exceptioncheckresult; 138 } 139 140 return $result; 141 } 142 143 return $this->runChainedSteps($env, $feature, $result, $skip); 144 } 145 146 /** 147 * Tears down step after a test. 148 * 149 * @param Environment $env 150 * @param FeatureNode $feature 151 * @param StepNode $step 152 * @param bool $skip 153 * @param StepResult $result 154 * @return Teardown 155 */ 156 public function tearDown(Environment $env, FeatureNode $feature, StepNode $step, $skip, StepResult $result) { 157 return $this->singlesteptester->tearDown($env, $feature, $step, $skip, $result); 158 } 159 160 /** 161 * Check if results supported. 162 * 163 * @param CallResult $result 164 * @return bool 165 */ 166 private function supportsResult(CallResult $result) { 167 $return = $result->getReturn(); 168 if ($return instanceof ChainedStep) { 169 return true; 170 } 171 if (!is_array($return) || empty($return)) { 172 return false; 173 } 174 foreach ($return as $value) { 175 if (!$value instanceof ChainedStep) { 176 return false; 177 } 178 } 179 return true; 180 } 181 182 /** 183 * Run chained steps. 184 * 185 * @param Environment $env 186 * @param FeatureNode $feature 187 * @param ExecutedStepResult $result 188 * @param bool $skip 189 * @return ExecutedStepResult|StepResult 190 */ 191 private function runChainedSteps(Environment $env, FeatureNode $feature, ExecutedStepResult $result, $skip) { 192 // Set chained setp is used, so it can be used by formatter to o/p. 193 self::$chainedstepused = true; 194 195 $callresult = $result->getCallResult(); 196 $steps = $callresult->getReturn(); 197 198 if (!is_array($steps)) { 199 // Test it, no need to dispatch events for single chain. 200 $stepresult = $this->test($env, $feature, $steps, $skip); 201 return $this->checkSkipResult($stepresult); 202 } 203 204 // Test all steps. 205 foreach ($steps as $step) { 206 // Setup new step. 207 $event = new BeforeStepTested($env, $feature, $step); 208 if (TestworkEventDispatcher::DISPATCHER_VERSION === 2) { 209 // Symfony 4.3 and up. 210 $this->eventdispatcher->dispatch($event, $event::BEFORE); 211 } else { 212 // TODO: Remove when our min supported version is >= 4.3. 213 $this->eventdispatcher->dispatch($event::BEFORE, $event); 214 } 215 216 $setup = $this->setUp($env, $feature, $step, $skip); 217 218 $event = new AfterStepSetup($env, $feature, $step, $setup); 219 if (TestworkEventDispatcher::DISPATCHER_VERSION === 2) { 220 // Symfony 4.3 and up. 221 $this->eventdispatcher->dispatch($event, $event::AFTER_SETUP); 222 } else { 223 // TODO: Remove when our min supported version is >= 4.3. 224 $this->eventdispatcher->dispatch($event::AFTER_SETUP, $event); 225 } 226 227 // Test it. 228 $stepresult = $this->test($env, $feature, $step, $skip); 229 230 // Tear down. 231 $event = new BeforeStepTeardown($env, $feature, $step, $result); 232 if (TestworkEventDispatcher::DISPATCHER_VERSION === 2) { 233 // Symfony 4.3 and up. 234 $this->eventdispatcher->dispatch($event, $event::BEFORE_TEARDOWN); 235 } else { 236 // TODO: Remove when our min supported version is >= 4.3. 237 $this->eventdispatcher->dispatch($event::BEFORE_TEARDOWN, $event); 238 } 239 240 $teardown = $this->tearDown($env, $feature, $step, $skip, $result); 241 242 $event = new AfterStepTested($env, $feature, $step, $result, $teardown); 243 if (TestworkEventDispatcher::DISPATCHER_VERSION === 2) { 244 // Symfony 4.3 and up. 245 $this->eventdispatcher->dispatch($event, $event::AFTER); 246 } else { 247 // TODO: Remove when our min supported version is >= 4.3. 248 $this->eventdispatcher->dispatch($event::AFTER, $event); 249 } 250 251 if (!$stepresult->isPassed()) { 252 return $this->checkSkipResult($stepresult); 253 } 254 } 255 return $this->checkSkipResult($stepresult); 256 } 257 258 /** 259 * Handle skip exception. 260 * 261 * @param StepResult $result 262 * 263 * @return ExecutedStepResult|SkippedStepResult 264 */ 265 private function checkSkipResult(StepResult $result) { 266 if ((method_exists($result, 'getException')) && ($result->getException() instanceof SkippedException)) { 267 return new SkippedStepResult($result->getSearchResult()); 268 } else { 269 return $result; 270 } 271 } 272 273 /** 274 * Returns if cahined steps are used. 275 * @return bool. 276 */ 277 public static function is_chained_step_used() { 278 return self::$chainedstepused; 279 } 280 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body