123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- <?php
- /**
- * @package Grav\Console\Cli
- *
- * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
- * @license MIT License; see LICENSE file for details.
- */
- namespace Grav\Console\Cli;
- use Grav\Common\Config\Config;
- use Grav\Common\File\CompiledYamlFile;
- use Grav\Common\Grav;
- use Grav\Common\Page\Interfaces\PageInterface;
- use Grav\Common\Page\Pages;
- use Grav\Console\GravCommand;
- use RocketTheme\Toolbox\Event\Event;
- use Symfony\Component\Console\Input\InputOption;
- use function in_array;
- use function is_object;
- /**
- * Class PageSystemValidatorCommand
- * @package Grav\Console\Cli
- */
- class PageSystemValidatorCommand extends GravCommand
- {
- /** @var array */
- protected $tests = [
- // Content
- 'header' => [[]],
- 'summary' => [[], [200], [200, true]],
- 'content' => [[]],
- 'getRawContent' => [[]],
- 'rawMarkdown' => [[]],
- 'value' => [['content'], ['route'], ['order'], ['ordering'], ['folder'], ['slug'], ['name'], /*['frontmatter'],*/ ['header.menu'], ['header.slug']],
- 'title' => [[]],
- 'menu' => [[]],
- 'visible' => [[]],
- 'published' => [[]],
- 'publishDate' => [[]],
- 'unpublishDate' => [[]],
- 'process' => [[]],
- 'slug' => [[]],
- 'order' => [[]],
- //'id' => [[]],
- 'modified' => [[]],
- 'lastModified' => [[]],
- 'folder' => [[]],
- 'date' => [[]],
- 'dateformat' => [[]],
- 'taxonomy' => [[]],
- 'shouldProcess' => [['twig'], ['markdown']],
- 'isPage' => [[]],
- 'isDir' => [[]],
- 'exists' => [[]],
- // Forms
- 'forms' => [[]],
- // Routing
- 'urlExtension' => [[]],
- 'routable' => [[]],
- 'link' => [[], [false], [true]],
- 'permalink' => [[]],
- 'canonical' => [[], [false], [true]],
- 'url' => [[], [true], [true, true], [true, true, false], [false, false, true, false]],
- 'route' => [[]],
- 'rawRoute' => [[]],
- 'routeAliases' => [[]],
- 'routeCanonical' => [[]],
- 'redirect' => [[]],
- 'relativePagePath' => [[]],
- 'path' => [[]],
- //'folder' => [[]],
- 'parent' => [[]],
- 'topParent' => [[]],
- 'currentPosition' => [[]],
- 'active' => [[]],
- 'activeChild' => [[]],
- 'home' => [[]],
- 'root' => [[]],
- // Translations
- 'translatedLanguages' => [[], [false], [true]],
- 'untranslatedLanguages' => [[], [false], [true]],
- 'language' => [[]],
- // Legacy
- 'raw' => [[]],
- 'frontmatter' => [[]],
- 'httpResponseCode' => [[]],
- 'httpHeaders' => [[]],
- 'blueprintName' => [[]],
- 'name' => [[]],
- 'childType' => [[]],
- 'template' => [[]],
- 'templateFormat' => [[]],
- 'extension' => [[]],
- 'expires' => [[]],
- 'cacheControl' => [[]],
- 'ssl' => [[]],
- 'metadata' => [[]],
- 'eTag' => [[]],
- 'filePath' => [[]],
- 'filePathClean' => [[]],
- 'orderDir' => [[]],
- 'orderBy' => [[]],
- 'orderManual' => [[]],
- 'maxCount' => [[]],
- 'modular' => [[]],
- 'modularTwig' => [[]],
- //'children' => [[]],
- 'isFirst' => [[]],
- 'isLast' => [[]],
- 'prevSibling' => [[]],
- 'nextSibling' => [[]],
- 'adjacentSibling' => [[]],
- 'ancestor' => [[]],
- //'inherited' => [[]],
- //'inheritedField' => [[]],
- 'find' => [['/']],
- //'collection' => [[]],
- //'evaluate' => [[]],
- 'folderExists' => [[]],
- //'getOriginal' => [[]],
- //'getAction' => [[]],
- ];
- /** @var Grav */
- protected $grav;
- /**
- * @return void
- */
- protected function configure(): void
- {
- $this
- ->setName('page-system-validator')
- ->setDescription('Page validator can be used to compare site before/after update and when migrating to Flex Pages.')
- ->addOption('record', 'r', InputOption::VALUE_NONE, 'Record results')
- ->addOption('check', 'c', InputOption::VALUE_NONE, 'Compare site against previously recorded results')
- ->setHelp('The <info>page-system-validator</info> command can be used to test the pages before and after upgrade');
- }
- /**
- * @return int
- */
- protected function serve(): int
- {
- $input = $this->getInput();
- $io = $this->getIO();
- $this->setLanguage('en');
- $this->initializePages();
- $io->newLine();
- $this->grav = $grav = Grav::instance();
- $grav->fireEvent('onPageInitialized', new Event(['page' => $grav['page']]));
- /** @var Config $config */
- $config = $grav['config'];
- if ($input->getOption('record')) {
- $io->writeln('Pages: ' . $config->get('system.pages.type', 'page'));
- $io->writeln('<magenta>Record tests</magenta>');
- $io->newLine();
- $results = $this->record();
- $file = $this->getFile('pages-old');
- $file->save($results);
- $io->writeln('Recorded tests to ' . $file->filename());
- } elseif ($input->getOption('check')) {
- $io->writeln('Pages: ' . $config->get('system.pages.type', 'page'));
- $io->writeln('<magenta>Run tests</magenta>');
- $io->newLine();
- $new = $this->record();
- $file = $this->getFile('pages-new');
- $file->save($new);
- $io->writeln('Recorded tests to ' . $file->filename());
- $file = $this->getFile('pages-old');
- $old = $file->content();
- $results = $this->check($old, $new);
- $file = $this->getFile('diff');
- $file->save($results);
- $io->writeln('Recorded results to ' . $file->filename());
- } else {
- $io->writeln('<green>page-system-validator [-r|--record] [-c|--check]</green>');
- }
- $io->newLine();
- return 0;
- }
- /**
- * @return array
- */
- private function record(): array
- {
- $io = $this->getIO();
- /** @var Pages $pages */
- $pages = $this->grav['pages'];
- $all = $pages->all();
- $results = [];
- $results[''] = $this->recordRow($pages->root());
- foreach ($all as $path => $page) {
- if (null === $page) {
- $io->writeln('<red>Error on page ' . $path . '</red>');
- continue;
- }
- $results[$page->rawRoute()] = $this->recordRow($page);
- }
- return json_decode(json_encode($results), true);
- }
- /**
- * @param PageInterface $page
- * @return array
- */
- private function recordRow(PageInterface $page): array
- {
- $results = [];
- foreach ($this->tests as $method => $params) {
- $params = $params ?: [[]];
- foreach ($params as $p) {
- $result = $page->$method(...$p);
- if (in_array($method, ['summary', 'content', 'getRawContent'], true)) {
- $result = preg_replace('/name="(form-nonce|__unique_form_id__)" value="[^"]+"/',
- 'name="\\1" value="DYNAMIC"', $result);
- $result = preg_replace('`src=("|\'|")/images/./././././[^"]+\\1`',
- 'src="\\1images/GENERATED\\1', $result);
- $result = preg_replace('/\?\d{10}/', '?1234567890', $result);
- } elseif ($method === 'httpHeaders' && isset($result['Expires'])) {
- $result['Expires'] = 'Thu, 19 Sep 2019 13:10:24 GMT (REPLACED AS DYNAMIC)';
- } elseif ($result instanceof PageInterface) {
- $result = $result->rawRoute();
- } elseif (is_object($result)) {
- $result = json_decode(json_encode($result), true);
- }
- $ps = [];
- foreach ($p as $val) {
- $ps[] = (string)var_export($val, true);
- }
- $pstr = implode(', ', $ps);
- $call = "->{$method}({$pstr})";
- $results[$call] = $result;
- }
- }
- return $results;
- }
- /**
- * @param array $old
- * @param array $new
- * @return array
- */
- private function check(array $old, array $new): array
- {
- $errors = [];
- foreach ($old as $path => $page) {
- if (!isset($new[$path])) {
- $errors[$path] = 'PAGE REMOVED';
- continue;
- }
- foreach ($page as $method => $test) {
- if (($new[$path][$method] ?? null) !== $test) {
- $errors[$path][$method] = ['old' => $test, 'new' => $new[$path][$method]];
- }
- }
- }
- return $errors;
- }
- /**
- * @param string $name
- * @return CompiledYamlFile
- */
- private function getFile(string $name): CompiledYamlFile
- {
- return CompiledYamlFile::instance('cache://tests/' . $name . '.yaml');
- }
- }
|