From d1e4cc3f4e0e64b162b11bdce2c36d6724675e2a Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 31 Mar 2017 22:07:49 -0700 Subject: [PATCH] CRM-20600 - PartialSyntaxTest - Ensure that Angular partials are well-formed --- Civi/Angular/Coder.php | 82 ++++++++++++ .../Civi/Angular/PartialSyntaxTest.php | 121 ++++++++++++++++++ tools/scripts/check-angular.php | 58 +++++++++ 3 files changed, 261 insertions(+) create mode 100644 Civi/Angular/Coder.php create mode 100644 tests/phpunit/Civi/Angular/PartialSyntaxTest.php create mode 100755 tools/scripts/check-angular.php diff --git a/Civi/Angular/Coder.php b/Civi/Angular/Coder.php new file mode 100644 index 0000000000..c780462c93 --- /dev/null +++ b/Civi/Angular/Coder.php @@ -0,0 +1,82 @@ +recode($html); + } + catch (\Exception $e) { + return FALSE; + } + + $htmlSig = preg_replace('/[ \t\r\n\/]+/', '', $this->cleanup($html)); + $docSig = preg_replace('/[ \t\r\n\/]+/', '', $recodedHtml); + if ($htmlSig !== $docSig || empty($html) != empty($htmlSig)) { + return FALSE; + } + return TRUE; + } + + /** + * Parse an HTML snippet and re-encode is as HTML. + * + * This is useful for detecting cases where the parser or encoder + * have quirks/bugs. + * + * @param string $html + * @return string + */ + public function recode($html) { + $doc = \phpQuery::newDocument("$html", 'text/html'); + return $this->encode($doc); + } + + /** + * Encode a phpQueryObject as HTML. + * + * @param \phpQueryObject $doc + * @return string + * HTML + */ + public function encode($doc) { + $doc->document->formatOutput = TRUE; + return $this->cleanup($doc->markupOuter()); + } + + protected function cleanup($html) { + $html = preg_replace_callback("/([\\-a-zA-Z0-9]+)=(')([^']*)(')/", array($this, 'cleanupAttribute'), $html); + $html = preg_replace_callback('/([\-a-zA-Z0-9]+)=(")([^"]*)(")/', array($this, 'cleanupAttribute'), $html); + return $html; + } + + protected function cleanupAttribute($matches) { + list ($full, $attr, $lquote, $value, $rquote) = $matches; + + switch ($attr) { + case 'href': + if (strpos($value, '%7B%7B') !== FALSE && strpos($value, '%7D%7D') !== FALSE) { + $value = urldecode($value); + } + break; + + default: + $value = html_entity_decode($value); + break; + } + + return "$attr=$lquote$value$rquote"; + } + +} diff --git a/tests/phpunit/Civi/Angular/PartialSyntaxTest.php b/tests/phpunit/Civi/Angular/PartialSyntaxTest.php new file mode 100644 index 0000000000..796345e2ae --- /dev/null +++ b/tests/phpunit/Civi/Angular/PartialSyntaxTest.php @@ -0,0 +1,121 @@ +useTransaction(TRUE); + parent::setUp(); + $this->createLoggedInUser(); + $this->res = \CRM_Core_Resources::singleton(); + $this->angular = new Manager($this->res); + } + + public function basicConsistencyExamples() { + $cases = array(); + + $cases[0] = array( + '
', + '
', + ); + $cases[1] = array( + '
', + '
', + ); + $cases[2] = array( + '
', + '
', + ); + $cases[3] = array( + '
', + '
', + ); + $cases[4] = array( + '
', + '
', + ); + $cases[5] = array( + '', + '', + ); + $cases[6] = array( + '
', + '
', + ); + + return $cases; + } + + /** + * @param string $inputHtml + * @param string $expectHtml + * @dataProvider basicConsistencyExamples + */ + public function testConsistencyExamples($inputHtml, $expectHtml) { + $coder = new Coder(); + $this->assertEquals($expectHtml, $coder->recode($inputHtml)); + } + + /** + */ + public function testAllPartials() { + $coder = new \Civi\Angular\Coder(); + $errors = array(); + $count = 0; + foreach ($this->angular->getModules() as $module => $moduleDefn) { + $partials = $this->angular->getPartials($module); + foreach ($partials as $path => $html) { + $count++; + if (!$coder->checkConsistentHtml($html)) { + $recodedHtml = $coder->recode($html); + $this->assertEquals($html, $recodedHtml, "File $path has inconsistent HTML. Use tools/scripts/check-angular.php to debug. "); + } + } + } + + $this->assertTrue($count > 0); + } + +} diff --git a/tools/scripts/check-angular.php b/tools/scripts/check-angular.php new file mode 100755 index 0000000000..b0d44d3003 --- /dev/null +++ b/tools/scripts/check-angular.php @@ -0,0 +1,58 @@ +#!/usr/bin/env php +\n"; + echo "example: cleanup-angular.php '/full/path/to/crmMailing/BlockSummary.html'\n"; + echo "note: The file path must be absolute.\n"; + exit(1); +} +else { + foreach ($files as $file) { + compareFile($file, $diffCmd); + } +} + +function compareFile($file, $diffCmd) { + $coder = new \Civi\Angular\Coder(); + + if (!file_exists($file)) { + fwrite(STDERR, "Failed to find file $file (CWD=" . getcwd() . ")\n"); + return; + } + $oldMarkup = file_get_contents($file); + if ($coder->checkConsistentHtml($oldMarkup)) { + echo "File \"$file\" appears sufficiently consistent.\n"; + } + else { + $newMarkup = $coder->recode($oldMarkup); + $newFile = "{$file}.recoded"; + echo "File \"$file\" appears to have consistency issues. Created $newFile.\n"; + file_put_contents($newFile, $newMarkup); + if ($diffCmd) { + passthru($diffCmd . ' ' . escapeshellarg($file) . ' ' . escapeshellarg($newFile)); + } + } +} \ No newline at end of file -- 2.25.1