Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* Copyright 2020 Adobe
* All Rights Reserved.
*/

declare(strict_types=1);

namespace Magento\Csp\Model\Collector\CspWhitelistXml;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Config\CompositeFileIteratorFactory;
use Magento\Framework\Config\FileResolverInterface;
use Magento\Framework\Filesystem;
use Magento\Framework\View\Design\ThemeInterface;
use Magento\Framework\View\DesignInterface;
use Magento\Framework\Filesystem\Directory\ReadInterface as DirectoryRead;
use Magento\Framework\View\Design\Theme\CustomizationInterface;
use Magento\Framework\View\Design\Theme\CustomizationInterfaceFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem\Directory\ReadInterface as DirectoryRead;
use Magento\Framework\Config\CompositeFileIteratorFactory;
use Magento\Framework\View\Design\ThemeInterface;
use Magento\Framework\View\DesignInterface;

/**
* Combines configuration files from both modules and current theme.
Expand Down Expand Up @@ -74,22 +74,29 @@ public function __construct(
*/
public function get($filename, $scope)
{
$configs = $this->moduleFileResolver->get($filename, $scope);
if ($scope === 'global') {
$files = [];
$theme = $this->theme;
while ($theme) {
/** @var CustomizationInterface $info */
$info = $this->themeInfoFactory->create(['theme' => $theme]);
$file = $info->getThemeFilesPath() .'/etc/' .$filename;
if ($this->rootDir->isExist($file)) {
$files[] = $file;
$configs = $this->moduleFileResolver->get($filename, $scope);

switch ($scope) {
case 'frontend':
case 'adminhtml':
$files = [];
$theme = $this->theme;
while ($theme) {
/** @var CustomizationInterface $info */
$info = $this->themeInfoFactory->create(['theme' => $theme]);
$file = $info->getThemeFilesPath() . '/etc/' . $filename;
if ($this->rootDir->isExist($file)) {
$files[] = $file;
}
$theme = $theme->getParentTheme();
}
$theme = $theme->getParentTheme();
}
$configs = $this->iteratorFactory->create(
['paths' => array_reverse($files), 'existingIterator' => $configs]
);
$configs = $this->iteratorFactory->create(
['paths' => array_reverse($files), 'existingIterator' => $configs]
);
break;
case 'global':
default:
break;
}

return $configs;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<?php
/**
* Copyright 2025 Adobe
* All Rights Reserved.
*/

declare(strict_types=1);

namespace Magento\Csp\Test\Unit\Model\Collector\CspWhitelistXml;

use Magento\Framework\Filesystem;
use Magento\Framework\View\Design\Theme\CustomizationInterface;
use Magento\Framework\View\Design\ThemeInterface;
use PHPUnit\Framework\TestCase;
use Magento\Framework\Config\FileResolverInterface;
use Magento\Csp\Model\Collector\CspWhitelistXml\FileResolver;
use Magento\Framework\View\DesignInterface;
use Magento\Framework\Config\CompositeFileIteratorFactory;
use Magento\Framework\View\Design\Theme\CustomizationInterfaceFactory;
use Magento\Framework\Filesystem\Directory\ReadInterface;
use Magento\Framework\App\Filesystem\DirectoryList;

class FileResolverTest extends TestCase
{
/**
* @var FileResolver
*/
private $model;

/**
* @var FileResolverInterface
*/
private $moduleFileResolverMock;

/**
* @var DesignInterface
*/
private $designMock;

/**
* @var CustomizationInterfaceFactory
*/
private $customizationFactoryMock;

/**
* @var Filesystem
*/
private $filesystemMock;

/**
* @var CompositeFileIteratorFactory
*/
private $iteratorFactoryMock;

/**
* @var ReadInterface
*/
private $readInterfaceMock;

/**
* @var ThemeInterface
*/
private $themeInterFaceMock;

/**
* @var CustomizationInterface
*/
private $customizationInterfaceMock;

protected function setUp(): void
{
$this->moduleFileResolverMock = $this->getMockBuilder(FileResolverInterface::class)
->disableOriginalConstructor()
->getMock();

$this->designMock = $this->getMockBuilder(DesignInterface::class)
->disableOriginalConstructor()
->getMock();

$this->themeInterFaceMock = $this->getMockBuilder(ThemeInterface::class)
->disableOriginalConstructor()
->getMock();

$this->designMock->expects($this->once())
->method('getDesignTheme')
->willReturn($this->themeInterFaceMock);

$this->customizationFactoryMock = $this->getMockBuilder(CustomizationInterfaceFactory::class)
->disableOriginalConstructor()
->onlyMethods(['create'])
->getMock();

$this->customizationInterfaceMock = $this->getMockBuilder(CustomizationInterface::class)
->disableOriginalConstructor()
->getMock();

$this->filesystemMock = $this->createPartialMock(Filesystem::class, ['getDirectoryRead']);

$this->readInterfaceMock = $this->getMockBuilder(ReadInterface::class)
->disableOriginalConstructor()
->getMock();

$this->filesystemMock->expects($this->once())
->method('getDirectoryRead')
->with(DirectoryList::ROOT)
->willReturn($this->readInterfaceMock);

$this->iteratorFactoryMock = $this->getMockBuilder(CompositeFileIteratorFactory::class)
->disableOriginalConstructor()
->getMock();

$this->model = new FileResolver(
$this->moduleFileResolverMock,
$this->designMock,
$this->customizationFactoryMock,
$this->filesystemMock,
$this->iteratorFactoryMock
);
}

/**
* Test for get method with frontend scope.
*
* @param string $scope
* @param string $fileName
* @param array $fileList
* @param string $themeFilesPath
*
* @return void
* @dataProvider providerGetFrontend
*/
public function testGetFrontend(string $scope, string $fileName, array $fileList, string $themeFilesPath): void
{
$this->moduleFileResolverMock->expects($this->once())
->method('get')
->with($fileName, $scope)
->willReturn($fileList);

$this->customizationFactoryMock->expects($this->any())
->method('create')
->with(['theme' => $this->themeInterFaceMock])
->willReturn($this->customizationInterfaceMock);

$this->customizationInterfaceMock->expects($this->once())
->method('getThemeFilesPath')
->willReturn($themeFilesPath);

$this->readInterfaceMock->expects($this->once())
->method('isExist')
->with($themeFilesPath.'/etc/'.$fileName)
->willReturn(true);

$this->iteratorFactoryMock->expects($this->once())
->method('create')
->with(
[
'paths' => array_reverse([$themeFilesPath.'/etc/'.$fileName]),
'existingIterator' => $fileList
]
)
->willReturn($fileList);

$this->assertEquals($fileList, $this->model->get($fileName, $scope));
}

/**
* Test for get method with global scope.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHPDoc comment signature is not complete

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

*
* @param string $scope
* @param string $fileName
* @param array $fileList
*
* @return void
* @dataProvider providerGetGlobal
*/
public function testGetGlobal(string $scope, string $fileName, array $fileList): void
{
$this->moduleFileResolverMock->expects($this->once())
->method('get')
->with($fileName, $scope)
->willReturn($fileList);
$this->assertEquals($fileList, $this->model->get($fileName, $scope));
}

/**
* Data provider for get global scope tests.
*
* @return array
*/
public static function providerGetGlobal(): array
{
return [
[
'global',
'csp_whitelist.xml',
['anyvendor/anymodule/etc/csp_whitelist.xml']
]
];
}

/**
* Data provider for get frontend & adminhtml scope tests.
*
* @return array
*/
public static function providerGetFrontend(): array
{
return [
[
'frontend',
'csp_whitelist.xml',
['themevendor/theme/etc/csp_whitelist.xml'],
'themevendor/theme'
],
[
'adminhtml',
'csp_whitelist.xml',
['adminthemevendor/admintheme/etc/csp_whitelist.xml'],
'adminthemevendor/admintheme'
]
];
}
}