Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
namespace Moodle\BehatExtension\Output\Formatter;
19
 
20
use Behat\Behat\EventDispatcher\Event\AfterStepTested;
21
use Behat\Behat\EventDispatcher\Event\BeforeScenarioTested;
22
use Behat\Behat\EventDispatcher\Event\BeforeStepTested;
23
use Behat\Testwork\Output\Formatter;
24
use Behat\Testwork\Output\Printer\OutputPrinter;
25
 
26
// phpcs:disable moodle.NamingConventions.ValidFunctionName.LowercaseMethod
27
 
28
/**
29
 * Feature step counter for distributing features between parallel runs.
30
 *
31
 * Use it with --dry-run (and any other selectors combination) to
32
 * get the results quickly.
33
 *
34
 * @package core
35
 * @copyright  2016 onwards Rajesh Taneja
36
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
38
class MoodleScreenshotFormatter implements Formatter {
39
 
40
    /** @var OutputPrinter */
41
    private $printer;
42
 
43
    /** @var array */
44
    private $parameters;
45
 
46
    /** @var string */
47
    private $name;
48
 
49
    /** @var string */
50
    private $description;
51
 
52
    /** @var int The scenario count */
53
    protected static $currentscenariocount = 0;
54
 
55
    /** @var int The step count within the current scenario */
56
    protected static $currentscenariostepcount = 0;
57
 
58
    /**
59
     * If we are saving any kind of dump on failure we should use the same parent dir during a run.
60
     *
61
     * @var The parent dir name
62
     */
63
    protected static $faildumpdirname = false;
64
 
65
    /**
66
     * Initializes formatter.
67
     *
68
     * @param string        $name
69
     * @param string        $description
70
     * @param array         $parameters
71
     * @param OutputPrinter $printer
72
     */
73
    public function __construct($name, $description, array $parameters, OutputPrinter $printer) {
74
        $this->name = $name;
75
        $this->description = $description;
76
        $this->parameters = $parameters;
77
        $this->printer = $printer;
78
    }
79
 
80
    /**
81
     * Returns an array of event names this subscriber wants to listen to.
82
     *
83
     * @return array The event names to listen to
84
     */
85
    public static function getSubscribedEvents() {
86
        return [
87
            'tester.scenario_tested.before'    => 'beforeScenario',
88
            'tester.step_tested.before'        => 'beforeStep',
89
            'tester.step_tested.after'         => 'afterStep',
90
        ];
91
    }
92
 
93
    /**
94
     * Returns formatter name.
95
     *
96
     * @return string
97
     */
98
    public function getName() {
99
        return $this->name;
100
    }
101
 
102
    /**
103
     * Returns formatter description.
104
     *
105
     * @return string
106
     */
107
    public function getDescription() {
108
        return $this->description;
109
    }
110
 
111
    /**
112
     * Returns formatter output printer.
113
     *
114
     * @return OutputPrinter
115
     */
116
    public function getOutputPrinter() {
117
        return $this->printer;
118
    }
119
 
120
    /**
121
     * Sets formatter parameter.
122
     *
123
     * @param string $name
124
     * @param mixed  $value
125
     */
126
    public function setParameter($name, $value) {
127
        $this->parameters[$name] = $value;
128
    }
129
 
130
    /**
131
     * Returns parameter name.
132
     *
133
     * @param string $name
134
     * @return mixed
135
     */
136
    public function getParameter($name) {
137
        return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
138
    }
139
 
140
    /**
141
     * Reset currentscenariostepcount
142
     *
143
     * @param BeforeScenarioTested $event
144
     */
145
    public function beforeScenario(BeforeScenarioTested $event) {
146
 
147
        self::$currentscenariostepcount = 0;
148
        self::$currentscenariocount++;
149
    }
150
 
151
    /**
152
     * Increment currentscenariostepcount
153
     *
154
     * @param BeforeStepTested $event
155
     */
156
    public function beforeStep(BeforeStepTested $event) {
157
        self::$currentscenariostepcount++;
158
    }
159
 
160
    /**
161
     * Take screenshot after step is executed.    Behat\Behat\Event\html
162
     *
163
     * @param AfterStepTested $event
164
     */
165
    public function afterStep(AfterStepTested $event) {
166
        $behathookcontext = $event->getEnvironment()->getContext('behat_hooks');
167
 
168
        $formats = $this->getParameter('formats');
169
        $formats = explode(',', $formats);
170
 
171
        // Take screenshot.
172
        if (in_array('image', $formats)) {
173
            $this->take_screenshot($event, $behathookcontext);
174
        }
175
 
176
        // Save html content.
177
        if (in_array('html', $formats)) {
178
            $this->take_contentdump($event, $behathookcontext);
179
        }
180
    }
181
 
182
    /**
183
     * Return screenshot directory where all screenshots will be saved.
184
     *
185
     * @return string
186
     */
187
    protected function get_run_screenshot_dir() {
188
        global $CFG;
189
 
190
        if (self::$faildumpdirname) {
191
            return self::$faildumpdirname;
192
        }
193
 
194
        // If output_path is set then use output_path else use faildump_path.
195
        if ($this->getOutputPrinter()->getOutputPath()) {
196
            $screenshotpath = $this->getOutputPrinter()->getOutputPath();
197
        } else if ($CFG->behat_faildump_path) {
198
            $screenshotpath = $CFG->behat_faildump_path;
199
        } else {
200
            // It should never reach here.
201
            throw new FormatterException('You should specify --out "SOME/PATH" for moodle_screenshot format');
202
        }
203
 
204
        if ($this->getParameter('dir_permissions')) {
205
            $dirpermissions = $this->getParameter('dir_permissions');
206
        } else {
207
            $dirpermissions = 0777;
208
        }
209
 
210
        // All the screenshot dumps should be in the same parent dir.
211
        self::$faildumpdirname = $screenshotpath . DIRECTORY_SEPARATOR . date('Ymd_His');
212
 
213
        if (!is_dir(self::$faildumpdirname) && !mkdir(self::$faildumpdirname, $dirpermissions, true)) {
214
            // It shouldn't, we already checked that the directory is writable.
215
            throw new FormatterException(sprintf(
216
                'No directories can be created inside %s, check the directory permissions.', $screenshotpath
217
            ));
218
        }
219
 
220
        return self::$faildumpdirname;
221
    }
222
 
223
    /**
224
     * Take screenshot when a step fails.
225
     *
226
     * @throws Exception
227
     * @param AfterStepTested $event
228
     * @param Context $context
229
     */
230
    protected function take_screenshot(AfterStepTested $event, $context) {
231
        // BrowserKit can't save screenshots.
232
        if ($context->getMink()->isSessionStarted($context->getMink()->getDefaultSessionName())) {
233
            if (get_class($context->getMink()->getSession()->getDriver()) === 'Behat\Mink\Driver\BrowserKitDriver') {
234
                return false;
235
            }
236
            list ($dir, $filename) = $this->get_faildump_filename($event, 'png');
237
            $context->saveScreenshot($filename, $dir);
238
        }
239
    }
240
 
241
    /**
242
     * Take a dump of the page content when a step fails.
243
     *
244
     * @throws Exception
245
     * @param AfterStepTested $event
246
     * @param \Behat\Context\Context\Context $context
247
     */
248
    protected function take_contentdump(AfterStepTested $event, $context) {
249
        list ($dir, $filename) = $this->get_faildump_filename($event, 'html');
250
        $fh = fopen($dir . DIRECTORY_SEPARATOR . $filename, 'w');
251
        fwrite($fh, $context->getMink()->getSession()->getPage()->getContent());
252
        fclose($fh);
253
    }
254
 
255
    /**
256
     * Determine the full pathname to store a failure-related dump.
257
     *
258
     * This is used for content such as the DOM, and screenshots.
259
     *
260
     * @param AfterStepTested $event
261
     * @param String $filetype The file suffix to use. Limited to 4 chars.
262
     */
263
    protected function get_faildump_filename(AfterStepTested $event, $filetype) {
264
        // Make a directory for the scenario.
265
        $featurename = $event->getFeature()->getTitle();
266
        $featurename = preg_replace('/([^a-zA-Z0-9\_]+)/', '-', $featurename);
267
        if ($this->getParameter('dir_permissions')) {
268
            $dirpermissions = $this->getParameter('dir_permissions');
269
        } else {
270
            $dirpermissions = 0777;
271
        }
272
 
273
        $dir = $this->get_run_screenshot_dir();
274
 
275
        // We want a i-am-the-scenario-title format.
276
        $dir = $dir . DIRECTORY_SEPARATOR . self::$currentscenariocount . '-' . $featurename;
277
        if (!is_dir($dir) && !mkdir($dir, $dirpermissions, true)) {
278
            // We already checked that the directory is writable. This should not fail.
279
            throw new FormatterException(sprintf(
280
                'No directories can be created inside %s, check the directory permissions.', $dir
281
            ));
282
        }
283
 
284
        // The failed step text.
285
        // We want a stepno-i-am-the-failed-step.$filetype format.
286
        $filename = $event->getStep()->getText();
287
        $filename = preg_replace('/([^a-zA-Z0-9\_]+)/', '-', $filename);
288
        $filename = self::$currentscenariostepcount . '-' . $filename;
289
 
290
        // File name limited to 255 characters. Leaving 4 chars for the file
291
        // extension as we allow .png for images and .html for DOM contents.
292
        $filename = substr($filename, 0, 250) . '.' . $filetype;
293
        return [$dir, $filename];
294
    }
295
}