xref: /dokuwiki/_test/tests/general/general_html.test.php (revision db97c5aeb9f52725d30de5cad40e227c4a804568)
1<?php
2
3/**
4 * Check some page output for validity
5 *
6 * @group internet
7 */
8class general_html_test extends DokuWikiTest
9{
10    /** @var string[] we consider these hits shortcomings in the validator and not errors */
11    protected $allowedErrors = [
12        'The string “ugc” is not a registered keyword.',
13        'skipping 1 heading level'
14    ];
15
16    /**
17     * List of requests to check for validity
18     *
19     * @return array
20     */
21    public function requestProvider()
22    {
23        return [
24            ['/doku.php', 'GET', []],
25            ['/doku.php', 'GET', ['do' => 'recent']],
26            ['/doku.php', 'GET', ['do' => 'index']],
27            ['/doku.php', 'GET', ['do' => 'login']],
28            ['/doku.php', 'GET', ['do' => 'search', 'q' => 'wiki']],
29            ['/doku.php', 'GET', ['id' => 'wiki:syntax']],
30            ['/doku.php', 'GET', ['id' => 'wiki:syntax', 'ns' => 'wiki', 'image' => 'wiki:dokuwiki-128.png', 'do' => 'media']],
31            ['/lib/exe/detail.php', 'GET', ['id' => 'wiki:syntax', 'media' => 'wiki:dokuwiki-128.png']],
32        ];
33    }
34
35    /**
36     * Sends the given HTML to the validator and returns the result
37     *
38     * @param string $html
39     * @return array
40     * @throws Exception when communication failed
41     */
42    protected function validate($html)
43    {
44        $http = new \dokuwiki\HTTP\DokuHTTPClient();
45        $http->headers['Content-Type'] = 'text/html; charset=utf-8';
46        $result = $http->post('https://validator.w3.org/nu/?out=json&level=error', $html);
47
48        if ($result === false) {
49            if($http->status == 429) {
50                throw new \Exception('Validator rejected requests because of too many requests');
51            } else {
52                throw new \Exception('Status: '. $http->status . ': ' . $http->error);
53            }
54        }
55
56        $result = json_decode($result, true);
57        if ($result === null) {
58            throw new \Exception('could not decode JSON');
59        }
60
61        return $result;
62    }
63
64    /**
65     * Reformat the errors for nicer display in output
66     *
67     * @param array $result
68     * @return string[]
69     */
70    protected function listErrors($result)
71    {
72        $errors = [];
73        foreach ($result['messages'] as $msg) {
74            if ($this->isAllowedError($msg['message'])) continue;
75            $errors[] = "☛ " . $msg['message'] . "\n" . $msg['extract'] . "\n";
76        }
77        return $errors;
78    }
79
80    /**
81     * Is the given string an allowed error that should be skipped?
82     *
83     * @param string $string
84     * @return bool
85     */
86    protected function isAllowedError($string)
87    {
88        $re = join('|', array_map('preg_quote_cb', $this->allowedErrors));
89        return (bool)preg_match("/$re/", $string);
90    }
91
92    /**
93     * @dataProvider requestProvider
94     * @param string $url
95     * @param string $method
96     * @param array $data
97     * @group internet
98     */
99    public function test_Validity($url, $method, $data)
100    {
101        $request = new TestRequest();
102        if ($method == 'GET') {
103            $response = $request->get($data, $url);
104        } elseif ($method == 'POST') {
105            $response = $request->post($data, $url);
106        } else {
107            throw new \RuntimeException("unknown method given: $method");
108        }
109
110        $html = $response->getContent();
111        try {
112            $result = $this->validate($html);
113        } catch (\Exception $e) {
114            $this->markTestSkipped($e->getMessage());
115            return;
116        }
117
118        $errors = $this->listErrors($result);
119        $info = "Invalid HTML found:\n" . join("\n", $errors);
120
121        $this->assertEquals(0, count($errors), $info);
122    }
123}
124