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