MDL-37046 behat: Moving installation instructions to MDocs
[moodle.git] / admin / tool / behat / locallib.php
CommitLineData
7f541ea3
DM
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/**
18 * Behat commands
19 *
20 * @package tool_behat
21 * @copyright 2012 David Monllaó
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25require_once($CFG->libdir . '/filestorage/file_exceptions.php');
26require_once($CFG->libdir . '/phpunit/bootstraplib.php');
33005f68 27require_once($CFG->libdir . '/phpunit/classes/tests_finder.php');
7f541ea3 28
6d994c51
DM
29/**
30 * Behat commands manager
31 *
32 * CLI + web execution
33 *
34 * @package tool_behat
35 * @copyright 2012 David Monllaó
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
7f541ea3
DM
38class tool_behat {
39
40 /**
41 * Displays basic info about acceptance tests
42 */
43 public static function info() {
44
33005f68
DM
45 $html = self::get_header();
46 $html .= self::get_info();
33005f68 47 $html .= self::get_switch_environment_form();
554820dd
DM
48
49 if (!self::is_test_environment_running()) {
50 $html .= self::get_steps_definitions_form();
51 $html .= self::get_run_tests_form();
52 }
53
33005f68 54 $html .= self::get_footer();
7f541ea3
DM
55
56 echo $html;
57 }
58
7f541ea3
DM
59 /**
60 * Lists the available steps definitions
6d994c51 61 * @param string $filter Keyword to filter the list of steps definitions availables
7f541ea3 62 */
6d994c51 63 public static function stepsdefinitions($filter = false) {
7f541ea3
DM
64 global $CFG;
65
33005f68
DM
66 if (!CLI_SCRIPT) {
67 confirm_sesskey();
68 }
69 self::check_behat_setup();
7f541ea3 70
6d994c51
DM
71 // Priority to the one specified as argument.
72 if (!$filter) {
73 $filter = optional_param('filter', false, PARAM_ALPHANUMEXT);
74 }
75
76 if ($filter) {
7f541ea3
DM
77 $filteroption = ' -d ' . $filter;
78 } else {
79 $filteroption = ' -di';
80 }
81
82 $currentcwd = getcwd();
83 chdir($CFG->behatpath);
84 exec('bin/behat' . $filteroption, $steps, $code);
85 chdir($currentcwd);
86
87 // Outputing steps.
33005f68
DM
88 $html = self::get_header();
89 $html .= self::get_steps_definitions_form($filter);
7f541ea3
DM
90
91 $content = '';
92 if ($steps) {
93 foreach ($steps as $line) {
94
95 // Skipping the step definition context.
96 if (strpos($line, '#') == 0) {
97 $content .= htmlentities($line) . '<br/>';
98 }
99 }
100 }
101
102 if ($content === '') {
103 $content = get_string('nostepsdefinitions', 'tool_behat');
104 }
105
106 $html .= html_writer::tag('div', $content, array('id' => 'steps-definitions'));
33005f68 107 $html .= self::get_footer();
cc544646 108
7f541ea3
DM
109 echo $html;
110 }
111
cc544646
DM
112 /**
113 * Switches from and to the regular environment to the testing environment
6d994c51 114 * @param string $testenvironment enable|disable
cc544646 115 */
6d994c51 116 public static function switchenvironment($testenvironment = false) {
cc544646
DM
117 global $CFG;
118
33005f68
DM
119 if (!CLI_SCRIPT) {
120 confirm_sesskey();
121 }
6d994c51
DM
122
123 // Priority to the one specified as argument.
124 if (!$testenvironment) {
125 $testenvironment = optional_param('testenvironment', 'enable', PARAM_ALPHA);
126 }
127
cc544646 128 if ($testenvironment == 'enable') {
33005f68 129 self::enable_test_environment();
cc544646 130 } else if ($testenvironment == 'disable') {
33005f68 131 self::disable_test_environment();
cc544646
DM
132 }
133
554820dd
DM
134 if (!CLI_SCRIPT) {
135 redirect(get_login_url());
136 }
cc544646 137 }
7f541ea3 138
7f541ea3
DM
139 /**
140 * Runs the acceptance tests
6d994c51
DM
141 * @param string $tags Restricts the executed tests to the ones that matches the tags
142 * @param string $extra Extra CLI behat options
7f541ea3 143 */
6d994c51 144 public static function runtests($tags = false, $extra = false) {
7f541ea3
DM
145 global $CFG;
146
33005f68
DM
147 if (!CLI_SCRIPT) {
148 confirm_sesskey();
149 }
150 self::check_behat_setup();
7f541ea3 151
8cdc0ce8
DM
152 self::update_config_file();
153
33005f68 154 echo self::get_header();
cc544646 155
7f541ea3
DM
156 @set_time_limit(0);
157
6d994c51
DM
158 // Priority to the one specified as argument.
159 if (!$tags) {
160 $tags = optional_param('tags', false, PARAM_ALPHANUMEXT);
161 }
162 // $extra only passed as CLI option (to simplify web runner usage).
163
7f541ea3 164 $tagsoption = '';
6d994c51 165 if ($tags) {
7f541ea3
DM
166 $tagsoption = ' --tags ' . $tags;
167 }
168
6d994c51
DM
169 if (!$extra && !CLI_SCRIPT) {
170 $extra = ' --format html';
171 } else if(!$extra && CLI_SCRIPT) {
172 $extra = '';
173 }
174
7f541ea3 175 // Switching database and dataroot to test environment.
33005f68 176 self::enable_test_environment();
7f541ea3
DM
177 $currentcwd = getcwd();
178
179 // Outputting runner form and tests results.
6d994c51
DM
180 if (!CLI_SCRIPT) {
181 echo self::get_run_tests_form($tags);
182 }
7f541ea3
DM
183
184 chdir($CFG->behatpath);
6d994c51 185 passthru('bin/behat ' . $tagsoption . ' ' .$extra, $code);
7f541ea3
DM
186
187 // Switching back to regular environment
33005f68 188 self::disable_test_environment();
7f541ea3 189 chdir($currentcwd);
cc544646 190
33005f68 191 echo self::get_footer();
7f541ea3
DM
192 }
193
8cdc0ce8
DM
194 /**
195 * Updates the config file
196 * @throws file_exception
197 */
198 private static function update_config_file() {
199
200 // Gets all the components with features
201 $components = tests_finder::get_components_with_tests('features');
202 if ($components) {
203 $contents = 'features:' . PHP_EOL;
204 foreach ($components as $componentname => $path) {
205 $contents .= ' - ' . $path . PHP_EOL;
206 }
207 }
208
209 // Gets all the components with steps definitions
210 // TODO
211
212 // Stores the file
213 $fullfilepath = self::get_behat_dir() . '/config.yml';
214 if (!file_put_contents($fullfilepath, $contents)) {
215 throw new file_exception('cannotcreatefile', $fullfilepath);
216 }
217 }
7f541ea3
DM
218
219 /**
220 * Checks whether the test database and dataroot is ready
221 * Stops execution if something went wrong
222 */
223 private static function test_environment_problem() {
224 global $CFG;
225
226 // phpunit --diag returns nothing if the test environment is set up correctly.
227 $currentcwd = getcwd();
228 chdir($CFG->dirroot . '/' . $CFG->admin . '/tool/phpunit/cli');
229 exec("php util.php --diag", $output, $code);
230 chdir($currentcwd);
231
232 // If something is not ready stop execution and display the CLI command output.
233 if ($code != 0) {
234 notice(implode(' ', $output));
235 }
236 }
237
238 /**
239 * Checks the behat setup
240 */
241 private static function check_behat_setup() {
242 global $CFG;
243
244 // Moodle setting.
245 if (empty($CFG->behatpath)) {
246 $msg = get_string('nobehatpath', 'tool_behat');
247 $url = $CFG->wwwroot . '/' . $CFG->admin . '/settings.php?section=systempaths';
248
249 if (!CLI_SCRIPT) {
250 $msg .= ' ' . html_writer::tag('a', get_string('systempaths', 'admin'), array('href' => $url));
251 }
252 notice($msg);
253 }
254
255 // Behat test command.
256 $currentcwd = getcwd();
257 chdir($CFG->behatpath);
258 exec('bin/behat --help', $output, $code);
259 chdir($currentcwd);
260
261 if ($code != 0) {
262 notice(get_string('wrongbehatsetup', 'tool_behat'));
263 }
264 }
265
266 /**
267 * Enables test mode checking the test environment setup
268 *
cc544646 269 * Stores a file in dataroot/behat to allow Moodle switch to
7f541ea3
DM
270 * test database and dataroot before the initial set up
271 *
272 * @throws file_exception
273 */
cc544646 274 private static function enable_test_environment() {
554820dd 275 global $CFG;
7f541ea3 276
33005f68 277 if (self::is_test_environment_enabled()) {
7f541ea3
DM
278 debugging('Test environment was already enabled');
279 return;
280 }
281
282 // Check that PHPUnit test environment is correctly set up.
33005f68 283 self::test_environment_problem();
7f541ea3 284
8cdc0ce8 285 $behatdir = self::get_behat_dir();
7f541ea3 286
33005f68 287 $contents = '$CFG->phpunit_prefix and $CFG->phpunit_dataroot are currently used as $CFG->prefix and $CFG->dataroot';
7f541ea3 288 $filepath = $behatdir . '/test_environment_enabled.txt';
33005f68 289 if (!file_put_contents($filepath, $contents)) {
7f541ea3
DM
290 throw new file_exception('cannotcreatefile', $filepath);
291 }
554820dd 292 chmod($filepath, $CFG->directorypermissions);
7f541ea3
DM
293 }
294
295 /**
296 * Disables test mode
297 */
cc544646 298 private static function disable_test_environment() {
7f541ea3 299
33005f68 300 $testenvfile = self::get_test_filepath();
7f541ea3 301
33005f68 302 if (!self::is_test_environment_enabled()) {
7f541ea3
DM
303 debugging('Test environment was already disabled');
304 } else {
cc544646
DM
305 if (!unlink($testenvfile)) {
306 throw new file_exception('cannotdeletetestenvironmentfile');
307 }
7f541ea3
DM
308 }
309 }
310
311 /**
312 * Checks whether test environment is enabled or disabled
cc544646
DM
313 *
314 * It does not return if the current script is running
315 * in test environment {@see tool_behat::is_test_environment_running()}
316 *
317 * @return bool
7f541ea3 318 */
554820dd 319 private static function is_test_environment_enabled() {
7f541ea3 320
33005f68 321 $testenvfile = self::get_test_filepath();
7f541ea3
DM
322 if (file_exists($testenvfile)) {
323 return true;
324 }
325
326 return false;
327 }
328
cc544646
DM
329 /**
330 * Returns true if Moodle is currently running with the test database and dataroot
331 * @return bool
332 */
554820dd 333 private static function is_test_environment_running() {
cc544646
DM
334 global $CFG;
335
336 if (!empty($CFG->originaldataroot)) {
337 return true;
338 }
339
340 return false;
341 }
342
343 /**
344 * Returns the path to the file which specifies if test environment is enabled
345 * @return string
346 */
347 private static function get_test_filepath() {
348 global $CFG;
349
33005f68 350 if (self::is_test_environment_running()) {
cc544646
DM
351 $testenvfile = $CFG->originaldataroot . '/behat/test_environment_enabled.txt';
352 } else {
353 $testenvfile = $CFG->dataroot . '/behat/test_environment_enabled.txt';
354 }
355
356 return $testenvfile;
357 }
358
8cdc0ce8
DM
359
360 /**
361 * Ensures the behat dir exists in moodledata
362 * @throws file_exception
363 * @return string Full path
364 */
365 private static function get_behat_dir() {
366 global $CFG;
367
368 $behatdir = $CFG->dataroot . '/behat';
369
370 if (!is_dir($behatdir)) {
371 if (!mkdir($behatdir, $CFG->directorypermissions, true)) {
372 throw new file_exception('storedfilecannotcreatefiledirs');
373 }
374 }
375
376 if (!is_writable($behatdir)) {
377 throw new file_exception('storedfilecannotcreatefiledirs');
378 }
379
380 return $behatdir;
381 }
382
cc544646
DM
383 /**
384 * Returns header output
385 * @return string
386 */
387 private static function get_header() {
388 global $OUTPUT;
389
33005f68
DM
390 $action = optional_param('action', 'info', PARAM_ALPHAEXT);
391
cc544646 392 if (CLI_SCRIPT) {
33005f68 393 return '';
cc544646
DM
394 }
395
cc544646
DM
396 $title = get_string('pluginname', 'tool_behat') . ' - ' . get_string('command' . $action, 'tool_behat');
397 $html = $OUTPUT->header();
398 $html .= $OUTPUT->heading($title);
399
400 return $html;
401 }
402
403 /**
404 * Returns footer output
405 * @return string
406 */
407 private static function get_footer() {
408 global $OUTPUT;
409
410 if (CLI_SCRIPT) {
411 return '';
412 }
413
414 return $OUTPUT->footer();
415 }
416
33005f68
DM
417 /**
418 * Returns a message and a button to continue if web execution
419 * @param string $html
420 * @param string $url
421 * @return string
422 */
423 private static function output_success($html, $url = false) {
424 global $CFG, $OUTPUT;
425
426 if (!$url) {
427 $url = $CFG->wwwroot . '/' . $CFG->admin . '/tool/behat/index.php';
428 }
429
430 if (!CLI_SCRIPT) {
431 $html = $OUTPUT->box($html, 'generalbox', 'notice');
432 $html .= $OUTPUT->continue_button($url);
433 }
434
435 return $html;
436 }
437
7f541ea3
DM
438 /**
439 * Returns the installation instructions
440 *
441 * (hardcoded in English)
442 *
443 * @return string
444 */
445 private static function get_info() {
446 global $OUTPUT;
447
554820dd
DM
448 $url = 'http://docs.moodle.org/dev/Behat_integration#Installation';
449
7f541ea3 450 $html = $OUTPUT->box_start();
554820dd
DM
451 $html .= html_writer::tag('h1', 'Installation info');
452 $html .= html_writer::tag('div', 'Follow <a href="' . $url . '" target="_blank">' . $url . '</a> instructions');
7f541ea3
DM
453 $html .= $OUTPUT->box_end();
454
455 return $html;
456 }
457
cc544646
DM
458 /**
459 * Returns a button to switch between environments
460 * @return string
461 */
462 private static function get_switch_environment_form() {
463 global $CFG, $OUTPUT;
464
33005f68 465 if (self::is_test_environment_running()) {
cc544646
DM
466 $perform = 'disable';
467 } else {
468 $perform = 'enable';
469 }
470 $params = array('action' => 'switchenvironment', 'testenvironment' => $perform, 'sesskey' => sesskey());
471 $url = new moodle_url('/' . $CFG->admin . '/tool/behat/index.php', $params);
472
473 $html = $OUTPUT->box_start();
474 $html .= '<p>' . get_string('switchenvironmentinfo', 'tool_behat') . '</p>';
475 $html .= $OUTPUT->single_button($url, get_string('switchenvironment' . $perform, 'tool_behat'));
476 $html .= $OUTPUT->box_end();
477
478 return $html;
479 }
480
7f541ea3
DM
481 /**
482 * Returns the steps definitions form
483 * @param string $filter To filter the steps definitions list by keyword
484 * @return string
485 */
486 private static function get_steps_definitions_form($filter = false) {
487 global $OUTPUT;
488
489 if ($filter === false) {
490 $filter = '';
491 } else {
492 $filter = s($filter);
493 }
494
495 $html = $OUTPUT->box_start();
496 $html .= '<form method="get" action="index.php">';
7f541ea3
DM
497 $html .= '<fieldset class="invisiblefieldset">';
498 $html .= '<label for="id_filter">Steps definitions which contains</label> ';
499 $html .= '<input type="text" id="id_filter" value="' . $filter . '" name="filter"/> (all steps definitions if empty)';
33005f68 500 $html .= '<p></p>';
7f541ea3
DM
501 $html .= '<input type="submit" value="View available steps definitions" />';
502 $html .= '<input type="hidden" name="action" value="stepsdefinitions" />';
503 $html .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
504 $html .= '</fieldset>';
505 $html .= '</form>';
506 $html .= $OUTPUT->box_end();
507
508 return $html;
509 }
510
511 /**
512 * Returns the run tests form
513 * @param string $tags To execute only the tests that matches the specified tag
514 * @return string
515 */
516 private static function get_run_tests_form($tags = false) {
517 global $OUTPUT;
518
519 if ($tags === false) {
520 $tags = '';
521 } else {
522 $tags = s($tags);
523 }
524
525 $html = $OUTPUT->box_start();
526 $html .= '<form method="get" action="index.php">';
7f541ea3
DM
527 $html .= '<fieldset class="invisiblefieldset">';
528 $html .= '<label for="id_tags">Tests tagged as</label> ';
529 $html .= '<input type="text" id="id_tags" value="' . $tags . '" name="tags"/> (all available tests if empty)';
33005f68 530 $html .= '<p></p>';
7f541ea3
DM
531 $html .= '<input type="submit" value="Run tests" />';
532 $html .= '<input type="hidden" name="action" value="runtests" />';
533 $html .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
534 $html .= '</fieldset>';
535 $html .= '</form>';
536 $html .= $OUTPUT->box_end();
537
538 return $html;
539 }
7f541ea3 540}