Completed
Pull Request — master (#8)
by Haralan
01:58
created

SaveOnFailure   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 94.44%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 3
dl 0
loc 185
ccs 51
cts 54
cp 0.9444
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A to_absolute_attribute() 0 4 1
A autocreate_directory() 0 10 3
A clear_directory() 0 8 4
A render_file() 0 9 1
A __construct() 0 12 2
B save_driver_content() 0 24 3
A addError() 0 10 4
A addFailure() 0 10 4
A addWarning() 0 3 1
A addRiskyTest() 0 3 1
A addIncompleteTest() 0 3 1
A addSkippedTest() 0 3 1
A startTest() 0 3 1
A endTest() 0 3 1
A startTestSuite() 0 3 1
A endTestSuite() 0 3 1
1
<?php
2
3
namespace Openbuildings\PHPUnitSpiderling;
4
5
class SaveOnFailure implements \PHPUnit\Framework\TestListener
6
{
7
    /**
8
     * Convert an attribute strigng from a relative to absolute, by providing a base_url.
9
     *
10
     * @param string $attribute name of the attribute, e.g. src, href
11
     * @param string $content   the string where to do the change
12
     * @param string $base_url
13
     *
14
     * @return string
15
     */
16 8
    public static function to_absolute_attribute($attribute, $content, $base_url = null)
17
    {
18 8
        return preg_replace('/('.$attribute.'=[\'"])\//', '$1'.rtrim($base_url, '/').'/', $content);
19
    }
20
21
    /**
22
     * Check if a directory does not exist, create it, otherwise check if it is writable.
23
     *
24
     * @param string $directory
25
     *
26
     * @throws Exception If directory is not writable
27
     */
28 2
    public static function autocreate_directory($directory)
29
    {
30 2
        if (!file_exists($directory)) {
31 2
            mkdir($directory, 0777, true);
32
        }
33
34 2
        if (!is_writable($directory)) {
35
            throw new \Exception("Directory \"{$directory}\" is not writable");
36
        }
37 2
    }
38
39
    /**
40
     * Delete all the files from a directory.
41
     *
42
     * @param string $directory
43
     */
44 2
    public static function clear_directory($directory)
45
    {
46 2
        foreach (scandir($directory) as $file) {
47 2
            if ($file !== '.' and $file !== '..') {
48 1
                unlink($directory.$file);
49
            }
50
        }
51 2
    }
52
53
    /**
54
     * Execute a php script and get the output of that script as a string, optionally pass variables as an associative array to be converted to local variables inside of the file.
55
     *
56
     * @param string $filename
57
     * @param array  $data
58
     *
59
     * @return string
60
     */
61 1
    public static function render_file($filename, array $data = [])
62
    {
63 1
        extract($data, EXTR_SKIP);
64
65 1
        ob_start();
66 1
        include $filename;
67
68 1
        return ob_get_clean();
69
    }
70
71
    protected $_directory;
72
    protected $_base_url;
73
74 1
    public function __construct($directory, $base_url)
75
    {
76 1
        if (!$directory) {
77
            throw new \Exception('You must set a directory to output errors to');
78
        }
79
80 1
        $this->_directory = rtrim($directory, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
81 1
        $this->_base_url = $base_url;
82
83 1
        self::autocreate_directory($directory);
84 1
        self::clear_directory($directory);
85 1
    }
86
87
    /**
88
     * Save the current content of the driver into an html file. Add javascript errors, messages and a title to the html content.
89
     *
90
     * @param \Openbuildings\Spiderling\Driver $driver
91
     * @param string                           $filename
92
     * @param string                           $title
93
     */
94 1
    public function save_driver_content(\Openbuildings\Spiderling\Driver $driver, $filename, $title)
95
    {
96 1
        $content = $driver->content();
97
98 1
        foreach (['href', 'action', 'src'] as $attribute) {
99 1
            $content = self::to_absolute_attribute($attribute, $content, $this->_base_url);
100
        }
101
102 1
        $testview = self::render_file(__DIR__.'/../assets/error-page.php', [
103 1
            'url' => $driver->current_url(),
104 1
            'title' => $title,
105 1
            'javascript_errors' => $driver->javascript_errors(),
106 1
            'javascript_messages' => $driver->javascript_messages(),
107
        ]);
108
109 1
        $page_content = str_replace('</body>', $testview.'</body>', $content);
110
111 1
        file_put_contents($this->_directory."/$filename.html", $page_content);
112
113
        try {
114 1
            $driver->screenshot($this->_directory."/$filename.png");
115
        } catch (\Openbuildings\Spiderling\Exception_Notimplemented $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
116
        }
117 1
    }
118
119
    /**
120
     * Implement PHPUnit\Framework\TestListener, save driver content if there was an error.
121
     *
122
     * @param \PHPUnit\Framework\Test $test
123
     * @param \Exception              $exception
124
     * @param int                     $time
125
     */
126 1
    public function addError(\PHPUnit\Framework\Test $test, \Exception $exception, $time)
127
    {
128 1
        if ($test instanceof TestCase and $test->is_driver_active() and $test->driver()->is_page_active()) {
129 1
            $this->save_driver_content(
130 1
                $test->driver(),
131 1
                get_class($test).'_'.$test->getName(false),
132 1
                $exception->getMessage()
133
            );
134
        }
135 1
    }
136
137
    /**
138
     * Implement PHPUnit\Framework\TestListener, save driver content if there was an error.
139
     *
140
     * @param \PHPUnit\Framework\Test                 $test
141
     * @param \PHPUnit\Framework\AssertionFailedError $failure
142
     * @param int                                     $time
143
     */
144 1
    public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $failure, $time)
145
    {
146 1
        if ($test instanceof TestCase and $test->is_driver_active() and $test->driver()->is_page_active()) {
147 1
            $this->save_driver_content(
148 1
                $test->driver(),
149 1
                get_class($test).'_'.$test->getName(false),
150 1
                $failure->getMessage()
151
            );
152
        }
153 1
    }
154
155
    // @codeCoverageIgnoreStart
156
    public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time)
157
    {
158
    }
159
160
    public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
161
    {
162
    }
163
164
    public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
165
    {
166
    }
167
168
    public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
169
    {
170
    }
171
172
    public function startTest(\PHPUnit\Framework\Test $test)
173
    {
174
    }
175
176
    public function endTest(\PHPUnit\Framework\Test $test, $time)
177
    {
178
    }
179
180
    public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
181
    {
182
    }
183
184
    public function endTestSuite(\PHPUnit\Framework\TestSuite $suite)
185
    {
186
    }
187
188
    // @codeCoverageIgnoreEnd
189
}
190