From bc634a570f6e2499f005c3a5935e05dd95c0e9f3 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Tue, 3 Mar 2015 20:10:55 +0100 Subject: [PATCH] Reworked TapParser to be compliant and more robust. Added another test case from #571. Updated the output of TapParser::processTestLine. Broke TapParser::parse down in simpler methods. TapParser: ignore leading garbage and properly complain on missing TAP log. TapParser: detect and report duplicated TAP log. TapParser: got rid of the "test" and "suite" values. They are only available with PHPUnit. TapParser: append the message from yaml diagnostic to existing message. Reworked the dispaly of test results. PHPUnit plugin: pretty print test data. --- PHPCI/Languages/lang.da.php | 10 +- PHPCI/Languages/lang.de.php | 10 +- PHPCI/Languages/lang.el.php | 10 +- PHPCI/Languages/lang.en.php | 10 +- PHPCI/Languages/lang.fr.php | 10 +- PHPCI/Languages/lang.it.php | 10 +- PHPCI/Languages/lang.nl.php | 10 +- PHPCI/Languages/lang.pl.php | 10 +- PHPCI/Languages/lang.ru.php | 10 +- PHPCI/Languages/lang.uk.php | 10 +- PHPCI/Plugin/Util/TapParser.php | 249 ++++++++++++++++------ Tests/PHPCI/Plugin/Util/TapParserTest.php | 240 ++++++++++++++++++++- public/assets/css/AdminLTE-custom.css | 9 +- public/assets/js/build-plugins/phpunit.js | 94 ++++++-- 14 files changed, 587 insertions(+), 105 deletions(-) diff --git a/PHPCI/Languages/lang.da.php b/PHPCI/Languages/lang.da.php index 4e61c3db..883e52b0 100644 --- a/PHPCI/Languages/lang.da.php +++ b/PHPCI/Languages/lang.da.php @@ -194,14 +194,20 @@ Services sektionen under dit Bitbucket-repository.', 'end' => 'Slut', 'from' => 'Fra', 'to' => 'Til', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultat', 'ok' => 'OK', 'took_n_seconds' => 'Tog %d sekunder', 'build_created' => 'Build Oprettet', 'build_started' => 'Build Startet', 'build_finished' => 'Build Afsluttet', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Navn', diff --git a/PHPCI/Languages/lang.de.php b/PHPCI/Languages/lang.de.php index a04e40e1..62504310 100644 --- a/PHPCI/Languages/lang.de.php +++ b/PHPCI/Languages/lang.de.php @@ -192,14 +192,20 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab 'end' => 'Ende', 'from' => 'Von', 'to' => 'Bis', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultat', 'ok' => 'OK', 'took_n_seconds' => 'Benötigte %d Sekunden', 'build_created' => 'Build erstellt', 'build_started' => 'Build gestartet', 'build_finished' => 'Build abgeschlossen', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Name', diff --git a/PHPCI/Languages/lang.el.php b/PHPCI/Languages/lang.el.php index 4b1e82ca..5afc6593 100644 --- a/PHPCI/Languages/lang.el.php +++ b/PHPCI/Languages/lang.el.php @@ -194,14 +194,20 @@ Services του Bitbucket αποθετηρίου σας.', 'end' => 'Τέλος', 'from' => 'Από', 'to' => 'Προς', - 'suite' => 'Σουίτα', - 'test' => 'Τέστ', 'result' => 'Αποτέλεσμα', 'ok' => 'ΟΚ', 'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα', 'build_created' => 'Η κατασκευή δημιουργήθηκε', 'build_started' => 'Η κατασκευή άρχισε', 'build_finished' => 'Η κατασκευή ολοκληρώθηκε', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Όνομα', diff --git a/PHPCI/Languages/lang.en.php b/PHPCI/Languages/lang.en.php index b23eff7b..393ef584 100644 --- a/PHPCI/Languages/lang.en.php +++ b/PHPCI/Languages/lang.en.php @@ -198,14 +198,20 @@ PHPCI', 'end' => 'End', 'from' => 'From', 'to' => 'To', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Result', 'ok' => 'OK', 'took_n_seconds' => 'Took %d seconds', 'build_created' => 'Build Created', 'build_started' => 'Build Started', 'build_finished' => 'Build Finished', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Name', diff --git a/PHPCI/Languages/lang.fr.php b/PHPCI/Languages/lang.fr.php index 7be361fc..6965cb06 100644 --- a/PHPCI/Languages/lang.fr.php +++ b/PHPCI/Languages/lang.fr.php @@ -195,14 +195,20 @@ PHPCI', 'end' => 'Fin', 'from' => 'À partir de', 'to' => 'jusque', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultat', 'ok' => 'OK', 'took_n_seconds' => 'Exécuté en %d secondes', 'build_created' => 'Build créé', 'build_started' => 'Build démarré', 'build_finished' => 'Build terminé', + 'test_message' => 'Message', + 'test_no_message' => 'Pas de message', + 'test_success' => 'Réussi(s): %d', + 'test_fail' => 'Echec(s): %d', + 'test_skipped' => 'Passé(s): %d', + 'test_error' => 'Erreurs: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Nom', diff --git a/PHPCI/Languages/lang.it.php b/PHPCI/Languages/lang.it.php index a8a3e13c..db6b23fc 100644 --- a/PHPCI/Languages/lang.it.php +++ b/PHPCI/Languages/lang.it.php @@ -197,14 +197,20 @@ PHPCI', 'end' => 'Finisci', 'from' => 'Da', 'to' => 'A', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Risultati', 'ok' => 'OK', 'took_n_seconds' => 'Sono stati impiegati %d seconds', 'build_created' => 'Build Creata', 'build_started' => 'Build Avviata', 'build_finished' => 'Build Terminata', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Nome', diff --git a/PHPCI/Languages/lang.nl.php b/PHPCI/Languages/lang.nl.php index 29e8b0f8..38e9466c 100644 --- a/PHPCI/Languages/lang.nl.php +++ b/PHPCI/Languages/lang.nl.php @@ -195,14 +195,20 @@ Services sectie van je Bitbucket repository toegevoegd worden.', 'end' => 'Einde', 'from' => 'Van', 'to' => 'Tot', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultaat', 'ok' => 'OK', 'took_n_seconds' => 'Duurde %d seconden', 'build_created' => 'Build aangemaakt', 'build_started' => 'Build gestart', 'build_finished' => 'Build beëindigd', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Naam', diff --git a/PHPCI/Languages/lang.pl.php b/PHPCI/Languages/lang.pl.php index 816eb009..b4f8430c 100644 --- a/PHPCI/Languages/lang.pl.php +++ b/PHPCI/Languages/lang.pl.php @@ -198,14 +198,20 @@ Services repozytoria Bitbucket.', 'end' => 'Koniec', 'from' => 'Od', 'to' => 'Do', - 'suite' => 'Zestaw ', - 'test' => 'Test', 'result' => 'Wynik', 'ok' => 'OK', 'took_n_seconds' => 'Zajęło %d sekund', 'build_created' => 'Budowanie Stworzone', 'build_started' => 'Budowanie Rozpoczęte', 'build_finished' => 'Budowanie Zakończone', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Nazwa', diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index 44ed1550..931c8910 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -193,14 +193,20 @@ PHPCI', 'end' => 'Конец', 'from' => 'Из', 'to' => 'В', - 'suite' => 'Комплект', - 'test' => 'Тест', 'result' => 'Результат', 'ok' => 'OK', 'took_n_seconds' => 'Заняло секунд: %d', 'build_created' => 'Сборка создана', 'build_started' => 'Сборка запущена', 'build_finished' => 'Сборка окончена', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Имя', diff --git a/PHPCI/Languages/lang.uk.php b/PHPCI/Languages/lang.uk.php index 3e96459e..b080c65a 100644 --- a/PHPCI/Languages/lang.uk.php +++ b/PHPCI/Languages/lang.uk.php @@ -195,14 +195,20 @@ PHPCI', 'end' => 'Кінець', 'from' => 'Від', 'to' => 'До', - 'suite' => 'Комплект', - 'test' => 'Тест', 'result' => 'Результат', 'ok' => 'OK', 'took_n_seconds' => 'Зайняло %d секунд', 'build_created' => 'Збірка створена', 'build_started' => 'Збірка розпочата', 'build_finished' => 'Збірка завершена', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Ім’я', diff --git a/PHPCI/Plugin/Util/TapParser.php b/PHPCI/Plugin/Util/TapParser.php index 57b92b55..18772a6a 100644 --- a/PHPCI/Plugin/Util/TapParser.php +++ b/PHPCI/Plugin/Util/TapParser.php @@ -2,7 +2,9 @@ namespace PHPCI\Plugin\Util; +use Exception; use PHPCI\Helper\Lang; +use Symfony\Component\Yaml\Yaml; /** * Processes TAP format strings into usable test result data. @@ -10,18 +12,41 @@ use PHPCI\Helper\Lang; */ class TapParser { - const TEST_COUNTS_PATTERN = '/([0-9]+)\.\.([0-9]+)/'; - const TEST_LINE_PATTERN = '/(ok|not ok)\s+[0-9]+\s+\-\s+([^\n]+)::([^\n]+)/'; - const TEST_MESSAGE_PATTERN = '/message\:\s+\'([^\']+)\'/'; - const TEST_COVERAGE_PATTERN = '/Generating code coverage report/'; - const TEST_SKIP_PATTERN = '/ok\s+[0-9]+\s+\-\s+#\s+SKIP/'; + const TEST_COUNTS_PATTERN = '/^\d+\.\.(\d+)/'; + const TEST_LINE_PATTERN = '/^(ok|not ok)(?:\s+\d+)?(?:\s+\-)?\s*(.*?)(?:\s*#\s*(skip|todo)\s*(.*))?\s*$/i'; + const TEST_YAML_START = '/^(\s*)---/'; + const TEST_DIAGNOSTIC = '/^#/'; /** * @var string */ protected $tapString; + + /** + * @var int + */ protected $failures = 0; + /** + * @var array + */ + protected $lines; + + /** + * @var integer + */ + protected $lineNumber; + + /** + * @var integer + */ + protected $testCount; + + /** + * @var array + */ + protected $results; + /** * Create a new TAP parser for a given string. * @param string $tapString The TAP format string to be parsed. @@ -38,81 +63,175 @@ class TapParser { // Split up the TAP string into an array of lines, then // trim all of the lines so there's no leading or trailing whitespace. - $lines = explode("\n", $this->tapString); - $lines = array_map(function ($line) { - return trim($line); - }, $lines); + $this->lines = array_map('rtrim', explode("\n", $this->tapString)); + $this->lineNumber = 0; - // Check TAP version: - $versionLine = array_shift($lines); + $this->testCount = false; + $this->results = array(); - if ($versionLine != 'TAP version 13') { - throw new \Exception(Lang::get('tap_version')); + $header = $this->findTapLog(); + + $line = $this->nextLine(); + if ($line === $header) { + throw new Exception("Duplicated TAP log, please check the configration."); } - if (isset($lines[count($lines) - 1]) && preg_match(self::TEST_COVERAGE_PATTERN, $lines[count($lines) - 1])) { - array_pop($lines); - if ($lines[count($lines) - 1] == "") { - array_pop($lines); + while ($line !== false && ($this->testCount === false || count($this->results) < $this->testCount)) { + $this->parseLine($line); + $line = $this->nextLine(); + } + + if (count($this->results) !== $this->testCount) { + throw new Exception(Lang::get('tap_error')); + } + + return $this->results; + } + + /** Looks for the start of the TAP log in the string. + * + * @return string The TAP header line. + * + * @throws Exception if no TAP log is found or versions mismatch. + */ + protected function findTapLog() + { + // Look for the beggning of the TAP output + do { + $header = $this->nextLine(); + } while ($header !== false && substr($header, 0, 12) !== 'TAP version '); + + // + if ($header === false) { + throw new Exception('No TAP log found, please check the configuration.'); + } elseif ($header !== 'TAP version 13') { + throw new Exception(Lang::get('tap_version')); + } + + return $header; + } + + /** Fetch the next line. + * + * @return string|false The next line or false if the end has been reached. + */ + protected function nextLine() + { + if ($this->lineNumber < count($this->lines)) { + return $this->lines[$this->lineNumber++]; + } + return false; + } + + /** Parse a single line. + * + * @param string $line + */ + protected function parseLine($line) + { + if (preg_match(self::TEST_COUNTS_PATTERN, $line, $matches)) { + $this->testCount = intval($matches[1]); + + } elseif (preg_match(self::TEST_DIAGNOSTIC, $line)) { + return; + + } elseif (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) { + $this->results[] = $this->processTestLine( + $matches[1], + isset($matches[2]) ? $matches[2] : '', + isset($matches[3]) ? $matches[3] : null, + isset($matches[4]) ? $matches[4] : null + ); + + } elseif (preg_match(self::TEST_YAML_START, $line, $matches)) { + $diagnostic = $this->processYamlBlock($matches[1]); + $test = array_pop($this->results); + if (isset($test['message'], $diagnostic['message'])) { + $test['message'] .= PHP_EOL . $diagnostic['message']; + unset($diagnostic['message']); } + $this->results[] = array_replace($test, $diagnostic); + + } else { + throw new Exception(sprintf('Incorrect TAP data, line %d: %s', $this->lineNumber, $line)); } - - $matches = array(); - $totalTests = 0; - if (preg_match(self::TEST_COUNTS_PATTERN, $lines[0], $matches)) { - array_shift($lines); - $totalTests = (int) $matches[2]; - } - - if (isset($lines[count($lines) - 1]) && - preg_match(self::TEST_COUNTS_PATTERN, $lines[count($lines) - 1], $matches)) { - array_pop($lines); - $totalTests = (int) $matches[2]; - } - - $rtn = $this->processTestLines($lines); - - if ($totalTests != count($rtn)) { - throw new \Exception(Lang::get('tap_error')); - } - - return $rtn; } /** - * Process the individual test lines within a TAP string. - * @param $lines + * Process an individual test line. + * + * @param string $result + * @param string $message + * @param string $directive + * @param string $reason + * * @return array */ - protected function processTestLines($lines) + protected function processTestLine($result, $message, $directive, $reason) { - $rtn = array(); + $test = array( + 'pass' => true, + 'message' => $message, + 'severity' => 'success', + ); - foreach ($lines as $line) { - $matches = array(); - - if (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) { - $ok = ($matches[1] == 'ok' ? true : false); - - if (!$ok) { - $this->failures++; - } - - $item = array( - 'pass' => $ok, - 'suite' => $matches[2], - 'test' => $matches[3], - ); - - $rtn[] = $item; - } elseif (preg_match(self::TEST_SKIP_PATTERN, $line, $matches)) { - $rtn[] = array('message' => 'SKIP'); - } elseif (preg_match(self::TEST_MESSAGE_PATTERN, $line, $matches)) { - $rtn[count($rtn) - 1]['message'] = $matches[1]; - } + if ($result !== 'ok') { + $test['pass'] = false; + $test['severity'] = substr($message, 0, 6) === 'Error:' ? 'error' : 'fail'; + $this->failures++; } - return $rtn; + if ($directive) { + $test = $this->processDirective($test, $directive, $reason); + } + + return $test; + } + + /** Process an indented Yaml block. + * + * @param string $indent The block indentation to ignore. + * + * @return array The processed Yaml content. + */ + protected function processYamlBlock($indent) + { + $startLine = $this->lineNumber+1; + $endLine = $indent.'...'; + $yamlLines = array(); + do { + $line = $this->nextLine(); + if ($line === false) { + throw new Exception(Lang::get('tap_error_endless_yaml', $startLine)); + } elseif ($line === $endLine) { + break; + } + $yamlLines[] = substr($line, strlen($indent)); + + } while (true); + + return Yaml::parse(join("\n", $yamlLines)); + } + + /** Process a TAP directive + * + * @param array $test + * @param string $directive + * @param string $reason + * @return array + */ + protected function processDirective($test, $directive, $reason) + { + $test['severity'] = strtolower($directive) === 'skip' ? 'skipped' : 'todo'; + + if (!empty($reason)) { + if (!empty($test['message'])) { + $test['message'] .= ', '.$test['severity'].': '; + } + $test['message'] .= $reason; + } + + return $test; } /** diff --git a/Tests/PHPCI/Plugin/Util/TapParserTest.php b/Tests/PHPCI/Plugin/Util/TapParserTest.php index c4438b79..75c04bfa 100644 --- a/Tests/PHPCI/Plugin/Util/TapParserTest.php +++ b/Tests/PHPCI/Plugin/Util/TapParserTest.php @@ -5,22 +5,254 @@ use PHPCI\Plugin\Util\TapParser; class TapParserTest extends \PHPUnit_Framework_TestCase { - public function testSkipped() + public function testSimple() + { + $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'success', 'message' => 'SomeTest::testAnother'), + array('pass' => false, 'severity' => 'fail', 'message' => ''), + ), $result); + + $this->assertEquals(1, $parser->getTotalFailures()); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessageRegExp /No TAP/ + */ + public function testNoTapData() + { + $content = <<parse(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessageRegExp /Duplicated TAP/ + */ + public function testDuplicateOutput() + { + $content = <<parse(); + } + + public function testSuiteAndTest() { $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'success', 'message' => 'SomeTest::testAnother',), + array('pass' => false, 'severity' => 'fail', 'message' => 'Failure: SomeTest::testAnother'), + array('pass' => false, 'severity' => 'error', 'message' => 'Error: SomeTest::testAnother'), + ), $result); + + $this->assertEquals(2, $parser->getTotalFailures()); + } + + + public function testSkipped() + { + $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'skipped', 'message' => ''), + array('pass' => true, 'severity' => 'skipped', 'message' => 'foobar'), + array('pass' => true, 'severity' => 'skipped', 'message' => 'foo, skipped: bar'), + ), $result); + + $this->assertEquals(0, $parser->getTotalFailures()); + } + + public function testTodo() + { + $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'todo', 'message' => 'SomeTest::testAnother, todo: really implement this test'), + array('pass' => true, 'severity' => 'todo', 'message' => 'really implement this test'), + array('pass' => true, 'severity' => 'todo', 'message' => 'this is a message, todo: really implement this test'), + array('pass' => true, 'severity' => 'todo', 'message' => ''), + ), $result); + + $this->assertEquals(0, $parser->getTotalFailures()); + } + + public function testYamlDiagnostic() + { + // From https://phpunit.de/manual/current/en/logging.html#logging.tap + $content = <<parse(); + + $this->assertEquals(array( + array( + 'pass' => false, + 'severity' => 'fail', + 'message' => 'FOO' . PHP_EOL . 'BAR', + ), + ), $result); + + $this->assertEquals(1, $parser->getTotalFailures()); + } + + public function testFailureAndError() + { + // From https://phpunit.de/manual/current/en/logging.html#logging.tap + $content = <<parse(); $this->assertEquals(array( - array('pass' => true, 'suite' => 'SomeTest', 'test' => 'testAnother'), - array('message' => 'SKIP'), + array( + 'pass' => false, + 'severity' => 'fail', + 'message' => 'Failure: testFailure::FailureErrorTest', + ), + array( + 'pass' => false, + 'severity' => 'error', + 'message' => 'Error: testError::FailureErrorTest', + ) ), $result); + $this->assertEquals(2, $parser->getTotalFailures()); + } + + /** + * @expectedException \Exception + */ + public function testGarbage() + { + $content = "Garbage !"; + + $parser = new TapParser($content); + $parser->parse(); + } + + /** + * @expectedException \Exception + */ + public function testInvalidTestCount() + { + $content = <<parse(); + } + + /** + * @expectedException \Exception + */ + public function testEndlessYaml() + { + $content = <<parse(); + } + + public function testCodeception() + { + $content = <<< TAP +TAP version 13 +ok 1 - try to access the dashboard as a guest (Auth/GuestAccessDashboardAndRedirectCept) +ok 2 - see the login page (Auth/SeeLoginCept) +ok 3 - click forgot password and see the email form (Auth/SeeLoginForgotPasswordCept) +ok 4 - see powered by runmybusiness branding (Auth/ShouldSeePoweredByBrandingCept) +ok 5 - submit invalid credentials (Auth/SubmitLoginAndFailCept) +ok 6 - submit valid credentials and see the dashboard (Auth/SubmitLoginAndSucceedCept) +1..6 +TAP; + + $parser = new TapParser($content); + $result = $parser->parse(); + + $this->assertEquals( + array( + array('pass' => true, 'severity' => 'success', 'message' => 'try to access the dashboard as a guest (Auth/GuestAccessDashboardAndRedirectCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'see the login page (Auth/SeeLoginCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'click forgot password and see the email form (Auth/SeeLoginForgotPasswordCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'see powered by runmybusiness branding (Auth/ShouldSeePoweredByBrandingCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'submit invalid credentials (Auth/SubmitLoginAndFailCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'submit valid credentials and see the dashboard (Auth/SubmitLoginAndSucceedCept)'), + ), + $result + ); + $this->assertEquals(0, $parser->getTotalFailures()); + } } diff --git a/public/assets/css/AdminLTE-custom.css b/public/assets/css/AdminLTE-custom.css index b95f7b70..e37a0f48 100644 --- a/public/assets/css/AdminLTE-custom.css +++ b/public/assets/css/AdminLTE-custom.css @@ -84,4 +84,11 @@ padding: 0; font-size: inherit; line-height: inherit; -} \ No newline at end of file +} + +#phpunit-data th div { margin: 0 0.5em; } +#phpunit-data .success td { background: none; color: #00a65a; } +#phpunit-data .fail td { background: none; color: #f56954; } +#phpunit-data .error td { background: none; color: #f56954; } +#phpunit-data .skipped td { background: none; color: #e08e0b; } +#phpunit-data .todo td { background: none; color: #00c0ef; } diff --git a/public/assets/js/build-plugins/phpunit.js b/public/assets/js/build-plugins/phpunit.js index 4a55d905..66f010a1 100644 --- a/public/assets/js/build-plugins/phpunit.js +++ b/public/assets/js/build-plugins/phpunit.js @@ -6,6 +6,13 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ displayOnUpdate: false, box: true, rendered: false, + statusMap: { + success : 'ok', + fail: 'remove', + error: 'warning-sign', + todo: 'info-sign', + skipped: 'exclamation-sign' + }, register: function() { var self = this; @@ -21,6 +28,11 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ query(); } }); + + $(document).on('click', '#phpunit-data .test-toggle', function(ev) { + var input = $(ev.target); + $('#phpunit-data tbody ' + input.data('target')).toggle(input.prop('checked')); + }); }, render: function() { @@ -28,7 +40,7 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ return $('
' + '' + '' + - ' ' + + ' ' + '' + '
'+Lang.get('test')+''+Lang.get('test_message')+'
'); }, @@ -43,7 +55,9 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ this.lastData = e.queryData; var tests = this.lastData[0].meta_value; + var thead = $('#phpunit-data thead tr'); var tbody = $('#phpunit-data tbody'); + thead.empty().append(''+Lang.get('test_message')+''); tbody.empty(); if (tests.length == 0) { @@ -51,24 +65,74 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ return; } + var counts = { success: 0, fail: 0, error: 0, skipped: 0, todo: 0 }, total = 0; + for (var i in tests) { - - var row = $('' + - ''+tests[i].suite+'' + - '::'+tests[i].test+'
' + - ''+(tests[i].message || '')+'' + - ''); - - if (!tests[i].pass) { - row.addClass('danger'); - } else { - row.addClass('success'); - } - - tbody.append(row); + var severity = tests[i].severity || 'success', + message = tests[i].message || ('' + Lang.get('test_no_message') + ''); + counts[severity]++; + total++; + tbody.append( + '' + + '' + + '
' + message + '
' + + (tests[i].data ? '
' + this.repr(tests[i].data) + '
' : '') + + '' + + '' + ); } + var checkboxes = $(''); + thead.append(checkboxes).append('' + Lang.get('test_total', total) + ''); + + for (var key in counts) { + var count = counts[key]; + if(count > 0) { + checkboxes.append( + '
 ' + + Lang.get('test_'+key, count)+ '
' + ); + } + } + + tbody.find('.success').hide(); + $('#build-phpunit-errors').show(); + }, + + repr: function(data) + { + switch(typeof(data)) { + case 'boolean': + return '' + (data ? 'true' : 'false') + ''; + case 'string': + return '"' + data + '"'; + case 'undefined': case null: + return 'null'; + case 'object': + var rows = []; + if(data instanceof Array) { + for(var i in data) { + rows.push('' + this.repr(data[i]) + ','); + } + } else { + for(var key in data) { + rows.push( + '' + + '' + this.repr(key) + '' + + '=>' + + '' + this.repr(data[key]) + ',' + + ''); + } + } + return '' + + '' + + rows.join('') + + '' + + '
array(
)
'; + } + return '???'; } });