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