123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Test\Integrity;
- use Magento\Framework\App\Filesystem\DirectoryList;
- /**
- * An integrity test that searches for references to static files and asserts that they are resolved via fallback
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- */
- class StaticFilesTest extends \PHPUnit\Framework\TestCase
- {
- /**
- * @var \Magento\Framework\View\Design\FileResolution\Fallback\StaticFile
- */
- private $fallback;
- /**
- * @var \Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple
- */
- private $explicitFallback;
- /**
- * @var \Magento\Framework\View\Design\Theme\FlyweightFactory
- */
- private $themeRepo;
- /**
- * @var \Magento\Framework\View\DesignInterface
- */
- private $design;
- /**
- * @var \Magento\Framework\View\Design\ThemeInterface
- */
- private $baseTheme;
- /**
- * @var \Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Alternative
- */
- private $alternativeResolver;
- /**
- * Factory for simple rule
- *
- * @var \Magento\Framework\View\Design\Fallback\Rule\SimpleFactory
- */
- private $simpleFactory;
- /**
- * @var \Magento\Framework\Filesystem
- */
- private $filesystem;
- protected function setUp()
- {
- $om = \Magento\TestFramework\Helper\Bootstrap::getObjectmanager();
- $this->fallback = $om->get(\Magento\Framework\View\Design\FileResolution\Fallback\StaticFile::class);
- $this->explicitFallback = $om->get(
- \Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple::class
- );
- $this->themeRepo = $om->get(\Magento\Framework\View\Design\Theme\FlyweightFactory::class);
- $this->design = $om->get(\Magento\Framework\View\DesignInterface::class);
- $this->baseTheme = $om->get(\Magento\Framework\View\Design\ThemeInterface::class);
- $this->alternativeResolver = $om->get(
- \Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Alternative::class
- );
- $this->simpleFactory = $om->get(\Magento\Framework\View\Design\Fallback\Rule\SimpleFactory::class);
- $this->filesystem = $om->get(\Magento\Framework\Filesystem::class);
- }
- /**
- * Scan references to files from other static files and assert they are correct
- *
- * The CSS or LESS files may refer to other resources using `import` or url() notation
- * We want to check integrity of all these references
- * Note that the references may have syntax specific to the Magento preprocessing subsystem
- *
- * @param string $area
- * @param string $themePath
- * @param string $locale
- * @param string $module
- * @param string $filePath
- * @param string $absolutePath
- * @dataProvider referencesFromStaticFilesDataProvider
- */
- public function testReferencesFromStaticFiles($area, $themePath, $locale, $module, $filePath, $absolutePath)
- {
- $contents = file_get_contents($absolutePath);
- preg_match_all(
- \Magento\Framework\View\Url\CssResolver::REGEX_CSS_RELATIVE_URLS,
- $contents,
- $matches
- );
- foreach ($matches[1] as $relatedResource) {
- if (false !== strpos($relatedResource, '@')) { // unable to parse paths with LESS variables/mixins
- continue;
- }
- list($relatedModule, $relatedPath) =
- \Magento\Framework\View\Asset\Repository::extractModule($relatedResource);
- if ($relatedModule) {
- $fallbackModule = $relatedModule;
- } else {
- if ('less' == pathinfo($filePath, PATHINFO_EXTENSION)) {
- /**
- * The LESS library treats the related resources with relative links not in the same way as CSS:
- * when another LESS file is included, it is embedded directly into the resulting document, but the
- * relative paths of related resources are not adjusted accordingly to the new root file.
- * Probably it is a bug of the LESS library.
- */
- $this->markTestSkipped("Due to LESS library specifics, the '{$relatedResource}' cannot be tested.");
- }
- $fallbackModule = $module;
- $relatedPath = \Magento\Framework\View\FileSystem::getRelatedPath($filePath, $relatedResource);
- }
- // the $relatedPath will be suitable for feeding to the fallback system
- $staticFile = $this->getStaticFile($area, $themePath, $locale, $relatedPath, $fallbackModule);
- if (empty($staticFile) && substr($relatedPath, 0, 2) === '..') {
- //check if static file exists on lib level
- $path = substr($relatedPath, 2);
- $libDir = rtrim($this->filesystem->getDirectoryRead(DirectoryList::LIB_WEB)->getAbsolutePath(), '/');
- $rule = $this->simpleFactory->create(['pattern' => $libDir]);
- $params = ['area' => $area, 'theme' => $themePath, 'locale' => $locale];
- $staticFile = $this->alternativeResolver->resolveFile($rule, $path, $params);
- }
- $this->assertNotEmpty(
- $staticFile,
- "The related resource cannot be resolved through fallback: '{$relatedResource}'"
- );
- }
- }
- /**
- * Get a default theme path for specified area
- *
- * @param string $area
- * @return string
- * @throws \LogicException
- */
- private function getDefaultThemePath($area)
- {
- switch ($area) {
- case 'frontend':
- return $this->design->getConfigurationDesignTheme($area);
- case 'adminhtml':
- return 'Magento/backend';
- case 'doc':
- return 'Magento/blank';
- default:
- throw new \LogicException('Unable to determine theme path');
- }
- }
- /**
- * Get static file through fallback system using specified params
- *
- * @param string $area
- * @param string|\Magento\Framework\View\Design\ThemeInterface $theme - either theme path (string) or theme object
- * @param string $locale
- * @param string $filePath
- * @param string $module
- * @param bool $isExplicit
- * @return bool|string
- */
- private function getStaticFile($area, $theme, $locale, $filePath, $module = null, $isExplicit = false)
- {
- if ($area == 'base') {
- $theme = $this->baseTheme;
- }
- if (!is_object($theme)) {
- $themePath = $theme ?: $this->getDefaultThemePath($area);
- $theme = $this->themeRepo->create($themePath, $area);
- }
- if ($isExplicit) {
- $type = \Magento\Framework\View\Design\Fallback\RulePool::TYPE_STATIC_FILE;
- return $this->explicitFallback->resolve($type, $filePath, $area, $theme, $locale, $module);
- }
- return $this->fallback->getFile($area, $theme, $locale, $filePath, $module);
- }
- /**
- * @return array
- */
- public function referencesFromStaticFilesDataProvider()
- {
- return \Magento\Framework\App\Utility\Files::init()->getStaticPreProcessingFiles('*.{less,css}');
- }
- /**
- * There must be either .css or .less file, because if there are both, then .less will not be found by fallback
- *
- * @param string $area
- * @param string $themePath
- * @param string $locale
- * @param string $module
- * @param string $filePath
- * @dataProvider lessNotConfusedWithCssDataProvider
- */
- public function testLessNotConfusedWithCss($area, $themePath, $locale, $module, $filePath)
- {
- if (false !== strpos($filePath, 'widgets.css')) {
- $filePath .= '';
- }
- $fileName = pathinfo($filePath, PATHINFO_FILENAME);
- $dirName = dirname($filePath);
- if ('.' == $dirName) {
- $dirName = '';
- } else {
- $dirName .= '/';
- }
- $cssPath = $dirName . $fileName . '.css';
- $lessPath = $dirName . $fileName . '.less';
- $cssFile = $this->getStaticFile($area, $themePath, $locale, $cssPath, $module, true);
- $lessFile = $this->getStaticFile($area, $themePath, $locale, $lessPath, $module, true);
- $this->assertFalse(
- $cssFile && $lessFile,
- "A resource file of only one type must exist. Both found: '$cssFile' and '$lessFile'"
- );
- }
- /**
- * @return array
- */
- public function lessNotConfusedWithCssDataProvider()
- {
- return \Magento\Framework\App\Utility\Files::init()->getStaticPreProcessingFiles('*.{less,css}');
- }
- /**
- * Test if references $this->getViewFileUrl() in .phtml-files are correct
- *
- * @param string $phtmlFile
- * @param string $area
- * @param string $themePath
- * @param string $fileId
- * @dataProvider referencesFromPhtmlFilesDataProvider
- */
- public function testReferencesFromPhtmlFiles($phtmlFile, $area, $themePath, $fileId)
- {
- list($module, $filePath) = \Magento\Framework\View\Asset\Repository::extractModule($fileId);
- $this->assertNotEmpty(
- $this->getStaticFile($area, $themePath, 'en_US', $filePath, $module),
- "Unable to locate '{$fileId}' reference from {$phtmlFile}"
- );
- }
- /**
- * @return array
- */
- public function referencesFromPhtmlFilesDataProvider()
- {
- $result = [];
- foreach (\Magento\Framework\App\Utility\Files::init()->getPhtmlFiles(true, false) as $info) {
- list($area, $themePath, , , $file) = $info;
- foreach ($this->collectGetViewFileUrl($file) as $fileId) {
- $result[] = [$file, $area, $themePath, $fileId];
- }
- }
- return $result;
- }
- /**
- * Find invocations of $block->getViewFileUrl() and extract the first argument value
- *
- * @param string $file
- * @return array
- */
- private function collectGetViewFileUrl($file)
- {
- $result = [];
- if (preg_match_all('/\$block->getViewFileUrl\(\'([^\']+?)\'\)/', file_get_contents($file), $matches)) {
- foreach ($matches[1] as $fileId) {
- $result[] = $fileId;
- }
- }
- return $result;
- }
- /**
- * @param string $layoutFile
- * @param string $area
- * @param string $themePath
- * @param string $fileId
- * @dataProvider referencesFromLayoutFilesDataProvider
- */
- public function testReferencesFromLayoutFiles($layoutFile, $area, $themePath, $fileId)
- {
- list($module, $filePath) = \Magento\Framework\View\Asset\Repository::extractModule($fileId);
- $this->assertNotEmpty(
- $this->getStaticFile($area, $themePath, 'en_US', $filePath, $module),
- "Unable to locate '{$fileId}' reference from layout XML in {$layoutFile}"
- );
- }
- /**
- * @return array
- */
- public function referencesFromLayoutFilesDataProvider()
- {
- $result = [];
- $files = \Magento\Framework\App\Utility\Files::init()->getLayoutFiles(['with_metainfo' => true], false);
- foreach ($files as $metaInfo) {
- list($area, $themePath, , , $file) = $metaInfo;
- foreach ($this->collectFileIdsFromLayout($file) as $fileId) {
- $result[] = [$file, $area, $themePath, $fileId];
- }
- }
- return $result;
- }
- /**
- * Collect view file declarations in layout XML-files
- *
- * @param string $file
- * @return array
- */
- private function collectFileIdsFromLayout($file)
- {
- $xml = simplexml_load_file($file);
- $elements = $xml->xpath('//head/css|link|script');
- $result = [];
- if ($elements) {
- foreach ($elements as $node) {
- $result[] = (string)$node;
- }
- }
- return $result;
- }
- }
|