Completed
Push — master ( 70754b...6c3c52 )
by Lukas Kahwe
08:13
created

Html5WebTestCase::getValidationServiceAvailable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * This file is part of the Liip/FunctionalTestBundle
5
 *
6
 * (c) Lukas Kahwe Smith <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Liip\FunctionalTestBundle\Test;
13
14
/**
15
 * @author Daniel Barsotti
16
 *
17
 * The on-line validator: https://validator.nu/
18
 * The documentation: https://about.validator.nu/
19
 * Documentation about the web service: https://github.com/validator/validator/wiki/Service:-HTTP-interface
20
 */
21
abstract class Html5WebTestCase extends WebTestCase
22
{
23
    // <title> can't be empty:
24
    //
25
    // HTML5 validation failed:
26
    //  Line 5: Element “title” must not be empty.
27
    protected $html5Wrapper = <<<'HTML'
28
<!DOCTYPE html>
29
<html>
30
<head>
31
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
32
    <title>HTML5</title>
33
</head>
34
<body>
35
<<CONTENT>>
36
</body>
37
</html>
38
HTML;
39
40
    protected $validationServiceAvailable = false;
41
42
    public function __construct($name = null, array $data = array(), $dataName = '')
43
    {
44
        parent::__construct($name, $data, $dataName);
45
46
        $this->validationServiceAvailable = $this->isValidationServiceAvailable();
47
    }
48
49
    /**
50
     * Check if the HTML5 validation service is available.
51
     */
52 1
    public function isValidationServiceAvailable()
53
    {
54 1
        $validationUrl = $this->getHtml5ValidatorServiceUrl();
55
56 1
        $ch = curl_init();
57 1
        curl_setopt($ch, CURLOPT_URL, $validationUrl);
58 1
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
59 1
        $res = curl_exec($ch);
60
61 1
        curl_close($ch);
62
63 1
        return $res !== false;
64
    }
65
66 1
    public function getValidationServiceAvailable()
67
    {
68 1
        return $this->validationServiceAvailable;
69
    }
70
71
    /**
72
     * Get the URL of the HTML5 validation service from the config.
73
     *
74
     * @return string
75
     */
76 1
    protected function getHtml5ValidatorServiceUrl()
77
    {
78 1
        return $this->getContainer()->getParameter('liip_functional_test.html5validation.url');
79
    }
80
81
    /**
82
     * Allows a subclass to set a custom HTML5 wrapper to test validity of HTML snippets.
83
     * The $wrapper may contain valid HTML5 code + the <<CONTENT>> placeholder.
84
     * This placeholder will be replaced by the tested snippet before validation.
85
     *
86
     * @param string $wrapper The custom HTML5 wrapper.
87
     */
88 1
    protected function setHtml5Wrapper($wrapper)
89
    {
90 1
        $this->html5Wrapper = $wrapper;
91 1
    }
92
93
    /**
94
     * Run the HTML5 validation on the content and returns the results as an array.
95
     *
96
     * @param string $content The HTML content to validate
97
     *
98
     * @return array|false
99
     */
100 1
    public function validateHtml5($content)
101
    {
102 1
        $validationUrl = $this->getHtml5ValidatorServiceUrl();
103
104 1
        $ch = curl_init();
105 1
        curl_setopt($ch, CURLOPT_URL, $validationUrl);
106 1
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
107 1
        curl_setopt($ch, CURLOPT_POST, true);
108 1
        curl_setopt($ch, CURLOPT_POSTFIELDS, array(
109 1
            'out' => 'json',
110 1
            'parser' => 'html5',
111 1
            'content' => $content,
112 1
        ));
113
114 1
        $res = curl_exec($ch);
115
116 1
        curl_close($ch);
117
118 1
        if ($res) {
119
            return json_decode($res);
120
        }
121
122
        // The HTML5 validation service could not be reached
123 1
        return false;
124
    }
125
126
    /**
127
     * Assert the content passed is valid HTML5. If yes, the function silently
128
     * runs, and if no it will make the test fail and display a summary of the
129
     * validation errors along with the line number where they occured.
130
     *
131
     * Warning: this function is completely agnostic about what it is actually
132
     * validating, thus it cannot display the validated URL. It is up to the
133
     * test programmer to use the $message parameter to add information about the
134
     * current URL. When the test fails, the message will be displayed in addition
135
     * to the error summary.
136
     *
137
     * @param string $content The content to validate
138
     * @param string $message An additional message to display when the test fails
139
     */
140 7
    public function assertIsValidHtml5($content, $message = '')
141
    {
142 7
        if (!$this->getValidationServiceAvailable()) {
143 1
            return $this->skipTestWithInvalidService();
144
        }
145
146 6
        $res = $this->validateHtml5($content);
147
        // json_decode returned null if the validator service didn't
148
        // return JSON
149 6
        if ((null === $res) || (false === $res) || (false === $res->messages)) {
150 1
            return $this->skipTestWithInvalidService();
151
        }
152
153 5
        $err_count = 0;
154 5
        $err_msg = 'HTML5 validation failed';
155 5
        if ($message != '') {
156 1
            $err_msg .= " [$message]";
157 1
        }
158 5
        $err_msg .= ":\n";
159
160 5
        $ignores = $this->getContainer()->getParameter('liip_functional_test.html5validation.ignores');
161
        /*
162
         * unfortunately, the bamboo html5 validator.nu gives back an empty "message" about the error with brightcove object, but we have to ignore the error
163
         * if our local validator.nu instance is fixed, this stuff should go away
164
         */
165 5
        $ignores_extract = $this->getContainer()->getParameter('liip_functional_test.html5validation.ignores_extract');
166
167 5
        foreach ($res->messages as $row) {
168 3
            if ($row->type == 'error') {
169 3
                foreach ($ignores as $ignore) {
170 1
                    if (preg_match($ignore, $row->message)) {
171 1
                        continue 2;
172
                    }
173 3
                }
174 3
                foreach ($ignores_extract as $ignore_extract) {
175 1
                    if (preg_match($ignore_extract, $row->extract)) {
176 1
                        continue 2;
177
                    }
178 3
                }
179
180 3
                ++$err_count;
181 3
                if (empty($row->message)) {
182 1
                    $err_msg .= "  Line {$row->lastLine}: Empty error message about {$row->extract}\n";
183 1
                } else {
184 3
                    $err_msg .= "  Line {$row->lastLine}: {$row->message}\n";
185
                }
186 3
            }
187 5
        }
188 5
        $this->assertTrue($err_count == 0, $err_msg);
189 2
    }
190
191
    /**
192
     * Assert the content passed is a valid HTML5 snippets (i.e. that the content,
193
     * wrapped into a basic HTML5 document body will pass the validation).
194
     *
195
     * @param string $snippet The snippet to validate
196
     * @param string $message An additional message to display when the test fails
197
     */
198 2
    public function assertIsValidHtml5Snippet($snippet, $message = '')
199
    {
200 2
        $content = str_replace('<<CONTENT>>', $snippet, $this->html5Wrapper);
201 2
        $this->assertIsValidHtml5($content, $message);
202 1
    }
203
204
    /**
205
     * Marks test as skipped with validation server error message.
206
     */
207 2
    protected function skipTestWithInvalidService()
208
    {
209 2
        $url = $this->getHtml5ValidatorServiceUrl();
210 2
        $this->markTestSkipped("HTML5 Validator service not found at '$url' !");
211
    }
212
}
213