diff options
Diffstat (limited to 'tests/lib/Template')
-rw-r--r-- | tests/lib/Template/CSSResourceLocatorTest.php | 123 | ||||
-rw-r--r-- | tests/lib/Template/JSCombinerTest.php | 538 | ||||
-rw-r--r-- | tests/lib/Template/JSResourceLocatorTest.php | 183 | ||||
-rw-r--r-- | tests/lib/Template/ResourceLocatorTest.php | 89 | ||||
-rw-r--r-- | tests/lib/Template/data/1.js | 1 | ||||
-rw-r--r-- | tests/lib/Template/data/1.js.license | 2 | ||||
-rw-r--r-- | tests/lib/Template/data/2.js | 1 | ||||
-rw-r--r-- | tests/lib/Template/data/2.js.license | 2 | ||||
-rw-r--r-- | tests/lib/Template/data/combine.json | 4 | ||||
-rw-r--r-- | tests/lib/Template/data/combine.json.license | 2 |
10 files changed, 945 insertions, 0 deletions
diff --git a/tests/lib/Template/CSSResourceLocatorTest.php b/tests/lib/Template/CSSResourceLocatorTest.php new file mode 100644 index 00000000000..2ae37999b32 --- /dev/null +++ b/tests/lib/Template/CSSResourceLocatorTest.php @@ -0,0 +1,123 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Template; + +use OC\AppConfig; +use OC\Files\AppData\AppData; +use OC\Files\AppData\Factory; +use OC\Template\CSSResourceLocator; +use OCA\Theming\ThemingDefaults; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IAppData; +use OCP\ICacheFactory; +use OCP\IConfig; +use OCP\IURLGenerator; +use Psr\Log\LoggerInterface; + +class CSSResourceLocatorTest extends \Test\TestCase { + /** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */ + protected $appData; + /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */ + protected $urlGenerator; + /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ + protected $config; + /** @var ThemingDefaults|\PHPUnit\Framework\MockObject\MockObject */ + protected $themingDefaults; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; + /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ + protected $logger; + /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $timeFactory; + /** @var AppConfig|\PHPUnit\Framework\MockObject\MockObject */ + private $appConfig; + + protected function setUp(): void { + parent::setUp(); + + $this->logger = $this->createMock(LoggerInterface::class); + $this->appData = $this->createMock(AppData::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->config = $this->createMock(IConfig::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->themingDefaults = $this->createMock(ThemingDefaults::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->appConfig = $this->createMock(AppConfig::class); + } + + private function cssResourceLocator() { + /** @var Factory|\PHPUnit\Framework\MockObject\MockObject $factory */ + $factory = $this->createMock(Factory::class); + $factory->method('get')->with('css')->willReturn($this->appData); + return new CSSResourceLocator( + $this->logger, + 'theme', + ['core' => 'map'], + ['3rd' => 'party'], + ); + } + + private function rrmdir($directory) { + $files = array_diff(scandir($directory), ['.','..']); + foreach ($files as $file) { + if (is_dir($directory . '/' . $file)) { + $this->rrmdir($directory . '/' . $file); + } else { + unlink($directory . '/' . $file); + } + } + return rmdir($directory); + } + + private function randomString() { + return sha1(uniqid(mt_rand(), true)); + } + + public function testFindWithAppPathSymlink(): void { + // First create new apps path, and a symlink to it + $apps_dirname = $this->randomString(); + $new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname; + $new_apps_path_symlink = $new_apps_path . '_link'; + mkdir($new_apps_path); + symlink($apps_dirname, $new_apps_path_symlink); + + // Create an app within that path + mkdir($new_apps_path . '/' . 'test-css-app'); + + // Use the symlink as the app path + \OC::$APPSROOTS[] = [ + 'path' => $new_apps_path_symlink, + 'url' => '/css-apps-test', + 'writable' => false, + ]; + + $locator = $this->cssResourceLocator(); + $locator->find(['test-css-app/test-file']); + + $resources = $locator->getResources(); + $this->assertCount(1, $resources); + $resource = $resources[0]; + $this->assertCount(3, $resource); + $root = $resource[0]; + $webRoot = $resource[1]; + $file = $resource[2]; + + $expectedRoot = $new_apps_path . '/test-css-app'; + $expectedWebRoot = \OC::$WEBROOT . '/css-apps-test/test-css-app'; + $expectedFile = 'test-file.css'; + + $this->assertEquals($expectedRoot, $root, + 'Ensure the app path symlink is resolved into the real path'); + $this->assertEquals($expectedWebRoot, $webRoot); + $this->assertEquals($expectedFile, $file); + + array_pop(\OC::$APPSROOTS); + unlink($new_apps_path_symlink); + $this->rrmdir($new_apps_path); + } +} diff --git a/tests/lib/Template/JSCombinerTest.php b/tests/lib/Template/JSCombinerTest.php new file mode 100644 index 00000000000..bc286695bc7 --- /dev/null +++ b/tests/lib/Template/JSCombinerTest.php @@ -0,0 +1,538 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Template; + +use OC\SystemConfig; +use OC\Template\JSCombiner; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\ITempManager; +use OCP\IURLGenerator; +use OCP\Server; +use Psr\Log\LoggerInterface; + +class JSCombinerTest extends \Test\TestCase { + /** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */ + protected $appData; + /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */ + protected $urlGenerator; + /** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject */ + protected $config; + /** @var ICache|\PHPUnit\Framework\MockObject\MockObject */ + protected $depsCache; + /** @var JSCombiner */ + protected $jsCombiner; + /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ + protected $logger; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; + + protected function setUp(): void { + parent::setUp(); + + $this->appData = $this->createMock(IAppData::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->config = $this->createMock(SystemConfig::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->depsCache = $this->createMock(ICache::class); + $this->cacheFactory->expects($this->atLeastOnce()) + ->method('createDistributed') + ->willReturn($this->depsCache); + $this->logger = $this->createMock(LoggerInterface::class); + $this->jsCombiner = new JSCombiner( + $this->appData, + $this->urlGenerator, + $this->cacheFactory, + $this->config, + $this->logger + ); + } + + public function testProcessDebugMode(): void { + $this->config + ->expects($this->once()) + ->method('getValue') + ->with('debug') + ->willReturn(true); + + $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp'); + $this->assertFalse($actual); + } + + public function testProcessNotInstalled(): void { + $this->config + ->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + ['debug', false], + ['installed', false] + ]); + + $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp'); + $this->assertFalse($actual); + } + + public function testProcessUncachedFileNoAppDataFolder(): void { + $this->config + ->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + ['debug', '', false], + ['installed', '', true], + ]); + $folder = $this->createMock(ISimpleFolder::class); + $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willThrowException(new NotFoundException()); + $this->appData->expects($this->once())->method('newFolder')->with('awesomeapp')->willReturn($folder); + $file = $this->createMock(ISimpleFile::class); + $gzfile = $this->createMock(ISimpleFile::class); + + $fileDeps = $this->createMock(ISimpleFile::class); + + $folder->method('getFile') + ->willReturnCallback(function ($path) use ($file, $gzfile) { + if ($path === 'combine.js') { + return $file; + } elseif ($path === 'combine.js.deps') { + throw new NotFoundException(); + } elseif ($path === 'combine.js.gzip') { + return $gzfile; + } + $this->fail(); + }); + $folder->expects($this->once()) + ->method('newFile') + ->with('combine.js.deps') + ->willReturn($fileDeps); + + $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp'); + $this->assertTrue($actual); + } + + public function testProcessUncachedFile(): void { + $this->config + ->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + ['debug', '', false], + ['installed', '', true], + ]); + $folder = $this->createMock(ISimpleFolder::class); + $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willReturn($folder); + $file = $this->createMock(ISimpleFile::class); + $fileDeps = $this->createMock(ISimpleFile::class); + $gzfile = $this->createMock(ISimpleFile::class); + + $folder->method('getFile') + ->willReturnCallback(function ($path) use ($file, $gzfile) { + if ($path === 'combine.js') { + return $file; + } elseif ($path === 'combine.js.deps') { + throw new NotFoundException(); + } elseif ($path === 'combine.js.gzip') { + return $gzfile; + } + $this->fail(); + }); + $folder->expects($this->once()) + ->method('newFile') + ->with('combine.js.deps') + ->willReturn($fileDeps); + + $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp'); + $this->assertTrue($actual); + } + + public function testProcessCachedFile(): void { + $this->config + ->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + ['debug', '', false], + ['installed', '', true], + ]); + $folder = $this->createMock(ISimpleFolder::class); + $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willReturn($folder); + $file = $this->createMock(ISimpleFile::class); + + $fileDeps = $this->createMock(ISimpleFile::class); + + $fileDeps->expects($this->once())->method('getContent')->willReturn('{}'); + + $folder->method('fileExists') + ->with('combine.js') + ->willReturn(true); + + $folder->method('getFile') + ->willReturnCallback(function ($path) use ($file, $fileDeps) { + if ($path === 'combine.js') { + return $file; + } + + if ($path === 'combine.js.deps') { + return $fileDeps; + } + + $this->fail(); + }); + + $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp'); + $this->assertTrue($actual); + } + + public function testProcessCachedFileMemcache(): void { + $this->config + ->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + ['debug', '', false], + ['installed', '', true], + ]); + $folder = $this->createMock(ISimpleFolder::class); + $this->appData->expects($this->once()) + ->method('getFolder') + ->with('awesomeapp') + ->willReturn($folder); + $folder->method('getName') + ->willReturn('awesomeapp'); + $folder->method('fileExists') + ->with('combine.js') + ->willReturn(true); + + $file = $this->createMock(ISimpleFile::class); + + $this->depsCache->method('get') + ->with('awesomeapp-combine.js.deps') + ->willReturn('{}'); + + $folder->method('getFile') + ->willReturnCallback(function ($path) use ($file) { + if ($path === 'combine.js') { + return $file; + } + $this->fail(); + }); + + $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp'); + $this->assertTrue($actual); + } + + public function testIsCachedNoDepsFile(): void { + $fileName = 'combine.json'; + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + + $folder->method('getFile') + ->willReturnCallback(function ($path) use ($file) { + if ($path === 'combine.js') { + return $file; + } + if ($path === 'combine.js.deps') { + throw new NotFoundException(); + } + $this->fail(); + }); + + $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]); + $this->assertFalse($actual); + } + + public function testIsCachedWithNotExistingFile(): void { + $fileName = 'combine.json'; + $folder = $this->createMock(ISimpleFolder::class); + $folder->method('fileExists') + ->with('combine.js') + ->willReturn(true); + $file = $this->createMock(ISimpleFile::class); + $folder->method('getFile') + ->with('combine.js.deps') + ->willReturn($file); + $file->expects($this->once()) + ->method('getContent') + ->willReturn(json_encode(['/etc/certainlynotexisting/file/ihope' => 10000])); + + $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]); + $this->assertFalse($actual); + } + + public function testIsCachedWithOlderMtime(): void { + $fileName = 'combine.json'; + $folder = $this->createMock(ISimpleFolder::class); + $folder->method('fileExists') + ->with('combine.js') + ->willReturn(true); + $file = $this->createMock(ISimpleFile::class); + $folder->method('getFile') + ->with('combine.js.deps') + ->willReturn($file); + $file->expects($this->once()) + ->method('getContent') + ->willReturn(json_encode([__FILE__ => 1234])); + + $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]); + $this->assertFalse($actual); + } + + public function testIsCachedWithoutContent(): void { + $fileName = 'combine.json'; + $folder = $this->createMock(ISimpleFolder::class); + $folder->method('fileExists') + ->with('combine.js') + ->willReturn(true); + $file = $this->createMock(ISimpleFile::class); + $folder->method('getFile') + ->with('combine.js.deps') + ->willReturn($file); + $file->expects($this->once()) + ->method('getContent') + ->willReturn(''); + $this->logger->expects($this->once()) + ->method('info') + ->with('JSCombiner: deps file empty: combine.js.deps'); + $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]); + $this->assertFalse($actual); + } + + public function testCacheNoFile(): void { + $fileName = 'combine.js'; + + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); + + $path = __DIR__ . '/data/'; + + $folder->method('getFile')->willThrowException(new NotFoundException()); + + $folder->method('newFile')->willReturnCallback( + function ($filename) use ($file, $depsFile, $gzFile) { + if ($filename === 'combine.js') { + return $file; + } elseif ($filename === 'combine.js.deps') { + return $depsFile; + } elseif ($filename === 'combine.js.gzip') { + return $gzFile; + } + $this->fail(); + } + ); + + $file->expects($this->once())->method('putContent'); + $depsFile->expects($this->once())->method('putContent'); + $gzFile->expects($this->once())->method('putContent'); + + $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); + $this->assertTrue($actual); + } + + public function testCache(): void { + $fileName = 'combine.js'; + + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); + + $path = __DIR__ . '/data/'; + + $folder->method('getFile')->willReturnCallback( + function ($filename) use ($file, $depsFile, $gzFile) { + if ($filename === 'combine.js') { + return $file; + } elseif ($filename === 'combine.js.deps') { + return $depsFile; + } elseif ($filename === 'combine.js.gzip') { + return $gzFile; + } + $this->fail(); + } + ); + + $file->expects($this->once())->method('putContent'); + $depsFile->expects($this->once())->method('putContent'); + $gzFile->expects($this->once())->method('putContent'); + + $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); + $this->assertTrue($actual); + } + + public function testCacheNotPermittedException(): void { + $fileName = 'combine.js'; + + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); + + $path = __DIR__ . '/data/'; + + $folder->expects($this->exactly(3)) + ->method('getFile') + ->willReturnMap([ + [$fileName, $file], + [$fileName . '.deps', $depsFile], + [$fileName . '.gzip', $gzFile] + ]); + + $file->expects($this->once()) + ->method('putContent') + ->with('var a = \'hello\'; + + +var b = \'world\'; + + +'); + $depsFile + ->expects($this->once()) + ->method('putContent') + ->with($this->callback( + function ($content) { + $deps = json_decode($content, true); + return array_key_exists(__DIR__ . '/data//1.js', $deps) + && array_key_exists(__DIR__ . '/data//2.js', $deps); + })) + ->willThrowException(new NotPermittedException()); + + $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); + $this->assertFalse($actual); + } + + public function testCacheSuccess(): void { + $fileName = 'combine.js'; + + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); + + $path = __DIR__ . '/data/'; + + + $folder->method('getFile')->willReturnCallback( + function ($filename) use ($file, $depsFile, $gzFile) { + if ($filename === 'combine.js') { + return $file; + } elseif ($filename === 'combine.js.deps') { + return $depsFile; + } elseif ($filename === 'combine.js.gzip') { + return $gzFile; + } + $this->fail(); + } + ); + + $file->expects($this->once()) + ->method('putContent') + ->with('var a = \'hello\'; + + +var b = \'world\'; + + +'); + $depsFile->expects($this->once())->method('putContent')->with($this->callback( + function ($content) { + $deps = json_decode($content, true); + return array_key_exists(__DIR__ . '/data//1.js', $deps) + && array_key_exists(__DIR__ . '/data//2.js', $deps); + })); + $gzFile->expects($this->once())->method('putContent')->with($this->callback( + function ($content) { + return gzdecode($content) === 'var a = \'hello\'; + + +var b = \'world\'; + + +'; + } + )); + + $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); + $this->assertTrue($actual); + } + + public static function dataGetCachedSCSS(): array { + return [ + ['awesomeapp', 'core/js/foo.json', '/js/core/foo.js'], + ['files', 'apps/files/js/foo.json', '/js/files/foo.js'] + ]; + } + + /** + * @param $appName + * @param $fileName + * @param $result + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetCachedSCSS')] + public function testGetCachedSCSS($appName, $fileName, $result): void { + $this->urlGenerator->expects($this->once()) + ->method('linkToRoute') + ->with('core.Js.getJs', [ + 'fileName' => 'foo.js', + 'appName' => $appName + ]) + ->willReturn(\OC::$WEBROOT . $result); + + $actual = $this->jsCombiner->getCachedJS($appName, $fileName); + $this->assertEquals(substr($result, 1), $actual); + } + + public function testGetContent(): void { + // Create temporary file with some content + $tmpFile = Server::get(ITempManager::class)->getTemporaryFile('JSCombinerTest'); + $pathInfo = pathinfo($tmpFile); + file_put_contents($tmpFile, json_encode(['/foo/bar/test', $pathInfo['dirname'] . '/js/mytest.js'])); + $tmpFilePathArray = explode('/', $pathInfo['basename']); + array_pop($tmpFilePathArray); + + $expected = [ + '//foo/bar/test', + '/' . implode('/', $tmpFilePathArray) . $pathInfo['dirname'] . '/js/mytest.js', + ]; + $this->assertEquals($expected, $this->jsCombiner->getContent($pathInfo['dirname'], $pathInfo['basename'])); + } + + public function testGetContentInvalidJson(): void { + // Create temporary file with some content + $tmpFile = Server::get(ITempManager::class)->getTemporaryFile('JSCombinerTest'); + $pathInfo = pathinfo($tmpFile); + file_put_contents($tmpFile, 'CertainlyNotJson'); + $expected = []; + $this->assertEquals($expected, $this->jsCombiner->getContent($pathInfo['dirname'], $pathInfo['basename'])); + } + + public function testResetCache(): void { + $file = $this->createMock(ISimpleFile::class); + $file->expects($this->once()) + ->method('delete'); + + $folder = $this->createMock(ISimpleFolder::class); + $folder->expects($this->once()) + ->method('getDirectoryListing') + ->willReturn([$file]); + + $cache = $this->createMock(ICache::class); + $this->cacheFactory->expects($this->once()) + ->method('createDistributed') + ->willReturn($cache); + $cache->expects($this->never()) + ->method('clear'); + $this->appData->expects($this->once()) + ->method('getDirectoryListing') + ->willReturn([$folder]); + + $this->jsCombiner->resetCache(); + } +} diff --git a/tests/lib/Template/JSResourceLocatorTest.php b/tests/lib/Template/JSResourceLocatorTest.php new file mode 100644 index 00000000000..89ab8e66dd7 --- /dev/null +++ b/tests/lib/Template/JSResourceLocatorTest.php @@ -0,0 +1,183 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Template; + +use OC\SystemConfig; +use OC\Template\JSCombiner; +use OC\Template\JSResourceLocator; +use OCP\App\AppPathNotFoundException; +use OCP\App\IAppManager; +use OCP\Files\IAppData; +use OCP\ICacheFactory; +use OCP\IURLGenerator; +use Psr\Log\LoggerInterface; + +class JSResourceLocatorTest extends \Test\TestCase { + /** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */ + protected $appData; + /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */ + protected $urlGenerator; + /** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject */ + protected $config; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; + /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ + protected $logger; + /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */ + protected $appManager; + + protected function setUp(): void { + parent::setUp(); + + $this->appData = $this->createMock(IAppData::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->config = $this->createMock(SystemConfig::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->appManager = $this->createMock(IAppManager::class); + } + + private function jsResourceLocator() { + $jsCombiner = new JSCombiner( + $this->appData, + $this->urlGenerator, + $this->cacheFactory, + $this->config, + $this->logger + ); + return new JSResourceLocator( + $this->logger, + $jsCombiner, + $this->appManager, + ); + } + + private function rrmdir($directory) { + $files = array_diff(scandir($directory), ['.','..']); + foreach ($files as $file) { + if (is_dir($directory . '/' . $file)) { + $this->rrmdir($directory . '/' . $file); + } else { + unlink($directory . '/' . $file); + } + } + return rmdir($directory); + } + + private function randomString() { + return sha1(uniqid(mt_rand(), true)); + } + + public function testFindWithAppPathSymlink(): void { + $appName = 'test-js-app'; + + // First create new apps path, and a symlink to it + $apps_dirname = $this->randomString(); + $new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname; + $new_apps_path_symlink = $new_apps_path . '_link'; + $this->assertTrue(( + mkdir($new_apps_path) && symlink($apps_dirname, $new_apps_path_symlink) + ), 'Setup of apps path failed'); + + // Create an app within that path + $this->assertTrue(( + mkdir($new_apps_path . '/' . $appName) && touch($new_apps_path . '/' . $appName . '/' . 'test-file.js') + ), 'Setup of app within the new apps path failed'); + + // Use the symlink as the app path + $this->appManager->expects($this->once()) + ->method('getAppPath') + ->with($appName) + ->willReturn("$new_apps_path_symlink/$appName"); + $this->appManager->expects($this->once()) + ->method('getAppWebPath') + ->with($appName) + ->willReturn("/js-apps-test/$appName"); + + // Run the tests + $locator = $this->jsResourceLocator(); + $locator->find(["$appName/test-file"]); + + $resources = $locator->getResources(); + $this->assertCount(1, $resources); + $resource = $resources[0]; + $this->assertCount(3, $resource); + $root = $resource[0]; + $webRoot = $resource[1]; + $file = $resource[2]; + + $expectedRoot = $new_apps_path; + $expectedWebRoot = \OC::$WEBROOT . '/js-apps-test'; + $expectedFile = $appName . '/test-file.js'; + + $this->assertEquals($expectedRoot, $root, + 'Ensure the app path symlink is resolved into the real path'); + $this->assertEquals($expectedWebRoot, $webRoot); + $this->assertEquals($expectedFile, $file); + + unlink($new_apps_path_symlink); + $this->rrmdir($new_apps_path); + } + + public function testNotExistingTranslationHandledSilent(): void { + $this->appManager->expects($this->once()) + ->method('getAppPath') + ->with('core') + ->willThrowException(new AppPathNotFoundException()); + $this->appManager->expects($this->atMost(1)) + ->method('getAppWebPath') + ->with('core') + ->willThrowException(new AppPathNotFoundException()); + // Assert logger is not called + $this->logger->expects($this->never()) + ->method('error'); + + // Run the tests + $locator = $this->jsResourceLocator(); + $locator->find(['core/l10n/en.js']); + + $resources = $locator->getResources(); + $this->assertCount(0, $resources); + } + + public function testFindModuleJSWithFallback(): void { + // First create new apps path, and a symlink to it + $apps_dirname = $this->randomString(); + $new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname; + mkdir($new_apps_path); + + // Create an app within that path + mkdir("$new_apps_path/test-js-app"); + touch("$new_apps_path/test-js-app/module.mjs"); + touch("$new_apps_path/test-js-app/both.mjs"); + touch("$new_apps_path/test-js-app/both.js"); + touch("$new_apps_path/test-js-app/plain.js"); + + // Use the app path + $this->appManager->expects($this->any()) + ->method('getAppPath') + ->with('test-js-app') + ->willReturn("$new_apps_path/test-js-app"); + + $locator = $this->jsResourceLocator(); + $locator->find(['test-js-app/module', 'test-js-app/both', 'test-js-app/plain']); + + $resources = $locator->getResources(); + $this->assertCount(3, $resources); + + $expectedWebRoot = \OC::$WEBROOT . '/js-apps-test/test-js-app'; + $expectedFiles = ['module.mjs', 'both.mjs', 'plain.js']; + + for ($idx = 0; $idx++; $idx < 3) { + $this->assertEquals($expectedWebRoot, $resources[$idx][1]); + $this->assertEquals($expectedFiles[$idx], $resources[$idx][2]); + } + + $this->rrmdir($new_apps_path); + } +} diff --git a/tests/lib/Template/ResourceLocatorTest.php b/tests/lib/Template/ResourceLocatorTest.php new file mode 100644 index 00000000000..599c8391ade --- /dev/null +++ b/tests/lib/Template/ResourceLocatorTest.php @@ -0,0 +1,89 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Template; + +use OC\SystemConfig; +use OC\Template\ResourceNotFoundException; +use Psr\Log\LoggerInterface; + +class ResourceLocatorTest extends \Test\TestCase { + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $logger; + + protected function setUp(): void { + parent::setUp(); + $this->logger = $this->createMock(LoggerInterface::class); + } + + /** + * @param string $theme + * @return \PHPUnit\Framework\MockObject\MockObject + */ + public function getResourceLocator($theme) { + $systemConfig = $this->createMock(SystemConfig::class); + $systemConfig + ->expects($this->any()) + ->method('getValue') + ->with('theme', '') + ->willReturn($theme); + $this->overwriteService(SystemConfig::class, $systemConfig); + return $this->getMockForAbstractClass('OC\Template\ResourceLocator', + [$this->logger], + '', true, true, true, []); + } + + public function testFind(): void { + $locator = $this->getResourceLocator('theme'); + $locator->expects($this->once()) + ->method('doFind') + ->with('foo'); + $locator->expects($this->once()) + ->method('doFindTheme') + ->with('foo'); + /** @var \OC\Template\ResourceLocator $locator */ + $locator->find(['foo']); + } + + public function testFindNotFound(): void { + $systemConfig = $this->createMock(SystemConfig::class); + $systemConfig->method('getValue') + ->with('theme', '') + ->willReturn('theme'); + $this->overwriteService(SystemConfig::class, $systemConfig); + $locator = $this->getResourceLocator('theme', + ['core' => 'map'], ['3rd' => 'party'], ['foo' => 'bar']); + $locator->expects($this->once()) + ->method('doFind') + ->with('foo') + ->willThrowException(new ResourceNotFoundException('foo', 'map')); + $locator->expects($this->once()) + ->method('doFindTheme') + ->with('foo') + ->willThrowException(new ResourceNotFoundException('foo', 'map')); + $this->logger->expects($this->exactly(2)) + ->method('debug') + ->with($this->stringContains('map/foo')); + /** @var \OC\Template\ResourceLocator $locator */ + $locator->find(['foo']); + } + + public function testAppendIfExist(): void { + $locator = $this->getResourceLocator('theme'); + /** @var \OC\Template\ResourceLocator $locator */ + $method = new \ReflectionMethod($locator, 'appendIfExist'); + $method->setAccessible(true); + + $method->invoke($locator, __DIR__, basename(__FILE__), 'webroot'); + $resource1 = [__DIR__, 'webroot', basename(__FILE__)]; + $this->assertEquals([$resource1], $locator->getResources()); + + $method->invoke($locator, __DIR__, 'does-not-exist'); + $this->assertEquals([$resource1], $locator->getResources()); + } +} diff --git a/tests/lib/Template/data/1.js b/tests/lib/Template/data/1.js new file mode 100644 index 00000000000..ab3d260180e --- /dev/null +++ b/tests/lib/Template/data/1.js @@ -0,0 +1 @@ +var a = 'hello'; diff --git a/tests/lib/Template/data/1.js.license b/tests/lib/Template/data/1.js.license new file mode 100644 index 00000000000..dddb428772d --- /dev/null +++ b/tests/lib/Template/data/1.js.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/tests/lib/Template/data/2.js b/tests/lib/Template/data/2.js new file mode 100644 index 00000000000..4fd3078d7ab --- /dev/null +++ b/tests/lib/Template/data/2.js @@ -0,0 +1 @@ +var b = 'world'; diff --git a/tests/lib/Template/data/2.js.license b/tests/lib/Template/data/2.js.license new file mode 100644 index 00000000000..dddb428772d --- /dev/null +++ b/tests/lib/Template/data/2.js.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/tests/lib/Template/data/combine.json b/tests/lib/Template/data/combine.json new file mode 100644 index 00000000000..e727cbdba8d --- /dev/null +++ b/tests/lib/Template/data/combine.json @@ -0,0 +1,4 @@ +[ + "1.js", + "2.js" +] diff --git a/tests/lib/Template/data/combine.json.license b/tests/lib/Template/data/combine.json.license new file mode 100644 index 00000000000..dddb428772d --- /dev/null +++ b/tests/lib/Template/data/combine.json.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors +SPDX-License-Identifier: AGPL-3.0-or-later |