aboutsummaryrefslogtreecommitdiffstats
path: root/apps/theming/tests
diff options
context:
space:
mode:
Diffstat (limited to 'apps/theming/tests')
-rw-r--r--apps/theming/tests/CapabilitiesTest.php135
-rw-r--r--apps/theming/tests/Controller/IconControllerTest.php86
-rw-r--r--apps/theming/tests/Controller/ThemingControllerTest.php468
-rw-r--r--apps/theming/tests/Controller/UserThemeControllerTest.php124
-rw-r--r--apps/theming/tests/IconBuilderTest.php114
-rw-r--r--apps/theming/tests/ImageManagerTest.php233
-rw-r--r--apps/theming/tests/Migration/Version2006Date20240905111627Test.php182
-rw-r--r--apps/theming/tests/Service/ThemesServiceTest.php369
-rw-r--r--apps/theming/tests/ServicesTest.php52
-rw-r--r--apps/theming/tests/Settings/AdminSectionTest.php60
-rw-r--r--apps/theming/tests/Settings/AdminTest.php114
-rw-r--r--apps/theming/tests/Settings/PersonalTest.php236
-rw-r--r--apps/theming/tests/Settings/SectionTest.php77
-rw-r--r--apps/theming/tests/Themes/AccessibleThemeTestCase.php172
-rw-r--r--apps/theming/tests/Themes/DarkHighContrastThemeTest.php130
-rw-r--r--apps/theming/tests/Themes/DarkThemeTest.php132
-rw-r--r--apps/theming/tests/Themes/DefaultThemeTest.php157
-rw-r--r--apps/theming/tests/Themes/DyslexiaFontTest.php163
-rw-r--r--apps/theming/tests/Themes/HighContrastThemeTest.php131
-rw-r--r--apps/theming/tests/ThemingDefaultsTest.php480
-rw-r--r--apps/theming/tests/UtilTest.php191
21 files changed, 2691 insertions, 1115 deletions
diff --git a/apps/theming/tests/CapabilitiesTest.php b/apps/theming/tests/CapabilitiesTest.php
index 1a11421ce1d..aa08a45a28b 100644
--- a/apps/theming/tests/CapabilitiesTest.php
+++ b/apps/theming/tests/CapabilitiesTest.php
@@ -1,41 +1,23 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Guillaume COMPAGNON <gcompagnon@outlook.com>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests;
use OCA\Theming\Capabilities;
+use OCA\Theming\ImageManager;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
use OCP\App\IAppManager;
use OCP\Files\IAppData;
use OCP\IConfig;
use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
/**
@@ -44,51 +26,53 @@ use Test\TestCase;
* @package OCA\Theming\Tests
*/
class CapabilitiesTest extends TestCase {
- /** @var ThemingDefaults|\PHPUnit\Framework\MockObject\MockObject */
- protected $theming;
-
- /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
- protected $url;
-
- /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
- protected $config;
-
- /** @var Util|\PHPUnit\Framework\MockObject\MockObject */
- protected $util;
-
- /** @var Capabilities */
- protected $capabilities;
+ protected ThemingDefaults&MockObject $theming;
+ protected IURLGenerator&MockObject $url;
+ protected IConfig&MockObject $config;
+ protected Util&MockObject $util;
+ protected IUserSession $userSession;
+ protected Capabilities $capabilities;
protected function setUp(): void {
parent::setUp();
$this->theming = $this->createMock(ThemingDefaults::class);
- $this->url = $this->getMockBuilder(IURLGenerator::class)->getMock();
+ $this->url = $this->createMock(IURLGenerator::class);
$this->config = $this->createMock(IConfig::class);
$this->util = $this->createMock(Util::class);
- $this->capabilities = new Capabilities($this->theming, $this->util, $this->url, $this->config);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->capabilities = new Capabilities(
+ $this->theming,
+ $this->util,
+ $this->url,
+ $this->config,
+ $this->userSession,
+ );
}
- public function dataGetCapabilities() {
+ public static function dataGetCapabilities(): array {
return [
- ['name', 'url', 'slogan', '#FFFFFF', '#000000', 'logo', 'background', 'http://absolute/', true, [
+ ['name', 'url', 'slogan', '#FFFFFF', '#000000', 'logo', 'background', '#fff', '#000', 'http://absolute/', true, [
'name' => 'name',
+ 'productName' => 'name',
'url' => 'url',
'slogan' => 'slogan',
'color' => '#FFFFFF',
'color-text' => '#000000',
- 'color-element' => '#aaaaaa',
- 'color-element-bright' => '#aaaaaa',
+ 'color-element' => '#b3b3b3',
+ 'color-element-bright' => '#b3b3b3',
'color-element-dark' => '#FFFFFF',
'logo' => 'http://absolute/logo',
'background' => 'http://absolute/background',
+ 'background-text' => '#000',
'background-plain' => false,
'background-default' => false,
'logoheader' => 'http://absolute/logo',
'favicon' => 'http://absolute/logo',
]],
- ['name1', 'url2', 'slogan3', '#01e4a0', '#ffffff', 'logo5', 'background6', 'http://localhost/', false, [
+ ['name1', 'url2', 'slogan3', '#01e4a0', '#ffffff', 'logo5', 'background6', '#fff', '#000', 'http://localhost/', false, [
'name' => 'name1',
+ 'productName' => 'name1',
'url' => 'url2',
'slogan' => 'slogan3',
'color' => '#01e4a0',
@@ -98,38 +82,43 @@ class CapabilitiesTest extends TestCase {
'color-element-dark' => '#01e4a0',
'logo' => 'http://localhost/logo5',
'background' => 'http://localhost/background6',
+ 'background-text' => '#000',
'background-plain' => false,
'background-default' => true,
'logoheader' => 'http://localhost/logo5',
'favicon' => 'http://localhost/logo5',
]],
- ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', 'http://localhost/', true, [
+ ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', '#000000', '#ffffff', 'http://localhost/', true, [
'name' => 'name1',
+ 'productName' => 'name1',
'url' => 'url2',
'slogan' => 'slogan3',
'color' => '#000000',
'color-text' => '#ffffff',
- 'color-element' => '#000000',
- 'color-element-bright' => '#000000',
- 'color-element-dark' => '#555555',
+ 'color-element' => '#4d4d4d',
+ 'color-element-bright' => '#4d4d4d',
+ 'color-element-dark' => '#4d4d4d',
'logo' => 'http://localhost/logo5',
'background' => '#000000',
+ 'background-text' => '#ffffff',
'background-plain' => true,
'background-default' => false,
'logoheader' => 'http://localhost/logo5',
'favicon' => 'http://localhost/logo5',
]],
- ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', 'http://localhost/', false, [
+ ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', '#000000', '#ffffff', 'http://localhost/', false, [
'name' => 'name1',
+ 'productName' => 'name1',
'url' => 'url2',
'slogan' => 'slogan3',
'color' => '#000000',
'color-text' => '#ffffff',
- 'color-element' => '#000000',
- 'color-element-bright' => '#000000',
- 'color-element-dark' => '#555555',
+ 'color-element' => '#4d4d4d',
+ 'color-element-bright' => '#4d4d4d',
+ 'color-element-dark' => '#4d4d4d',
'logo' => 'http://localhost/logo5',
'background' => '#000000',
+ 'background-text' => '#ffffff',
'background-plain' => true,
'background-default' => true,
'logoheader' => 'http://localhost/logo5',
@@ -139,19 +128,10 @@ class CapabilitiesTest extends TestCase {
}
/**
- * @dataProvider dataGetCapabilities
- * @param string $name
- * @param string $url
- * @param string $slogan
- * @param string $color
- * @param string $textColor
- * @param string $logo
- * @param string $background
- * @param string $baseUrl
- * @param bool $backgroundThemed
- * @param string[] $expected
+ * @param non-empty-array<string, string> $expected
*/
- public function testGetCapabilities($name, $url, $slogan, $color, $textColor, $logo, $background, $baseUrl, $backgroundThemed, array $expected) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetCapabilities')]
+ public function testGetCapabilities(string $name, string $url, string $slogan, string $color, string $textColor, string $logo, string $background, string $backgroundColor, string $backgroundTextColor, string $baseUrl, bool $backgroundThemed, array $expected): void {
$this->config->expects($this->once())
->method('getAppValue')
->willReturn($background);
@@ -159,29 +139,38 @@ class CapabilitiesTest extends TestCase {
->method('getName')
->willReturn($name);
$this->theming->expects($this->once())
+ ->method('getProductName')
+ ->willReturn($name);
+ $this->theming->expects($this->once())
->method('getBaseUrl')
->willReturn($url);
$this->theming->expects($this->once())
->method('getSlogan')
->willReturn($slogan);
+ $this->theming->expects($this->once())
+ ->method('getColorBackground')
+ ->willReturn($backgroundColor);
+ $this->theming->expects($this->once())
+ ->method('getTextColorBackground')
+ ->willReturn($backgroundTextColor);
$this->theming->expects($this->atLeast(1))
- ->method('getColorPrimary')
+ ->method('getDefaultColorPrimary')
->willReturn($color);
$this->theming->expects($this->exactly(3))
->method('getLogo')
->willReturn($logo);
- $this->theming->expects($this->once())
- ->method('getTextColorPrimary')
- ->willReturn($textColor);
- $util = new Util($this->config, $this->createMock(IAppManager::class), $this->createMock(IAppData::class));
+ $util = new Util($this->createMock(ServerVersion::class), $this->config, $this->createMock(IAppManager::class), $this->createMock(IAppData::class), $this->createMock(ImageManager::class));
$this->util->expects($this->exactly(3))
->method('elementColor')
->with($color)
- ->willReturnCallback(static function (string $color, bool $brightBackground = true) use ($util) {
+ ->willReturnCallback(static function (string $color, ?bool $brightBackground = null) use ($util) {
return $util->elementColor($color, $brightBackground);
});
+ $this->util->expects($this->any())
+ ->method('invertTextColor')
+ ->willReturnCallback(fn () => $textColor === '#000000');
$this->util->expects($this->once())
->method('isBackgroundThemed')
->willReturn($backgroundThemed);
diff --git a/apps/theming/tests/Controller/IconControllerTest.php b/apps/theming/tests/Controller/IconControllerTest.php
index a40809855d1..c5034600e03 100644
--- a/apps/theming/tests/Controller/IconControllerTest.php
+++ b/apps/theming/tests/Controller/IconControllerTest.php
@@ -1,31 +1,10 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Haertl <jus@bitgrid.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Michael Weimann <mail@michael-weimann.eu>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests\Controller;
use OC\Files\SimpleFS\SimpleFile;
@@ -34,32 +13,25 @@ use OCA\Theming\Controller\IconController;
use OCA\Theming\IconBuilder;
use OCA\Theming\ImageManager;
use OCA\Theming\ThemingDefaults;
+use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\NotFoundException;
-use OCP\IConfig;
use OCP\IRequest;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class IconControllerTest extends TestCase {
- /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */
- private $request;
- /** @var ThemingDefaults|\PHPUnit\Framework\MockObject\MockObject */
- private $themingDefaults;
- /** @var \OCP\AppFramework\Utility\ITimeFactory */
- private $timeFactory;
- /** @var IconController|\PHPUnit\Framework\MockObject\MockObject */
- private $iconController;
- /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
- private $config;
- /** @var IconBuilder|\PHPUnit\Framework\MockObject\MockObject */
- private $iconBuilder;
- /** @var FileAccessHelper|\PHPUnit\Framework\MockObject\MockObject */
- private $fileAccessHelper;
- /** @var ImageManager */
- private $imageManager;
+ private IRequest&MockObject $request;
+ private ThemingDefaults&MockObject $themingDefaults;
+ private ITimeFactory&MockObject $timeFactory;
+ private IconBuilder&MockObject $iconBuilder;
+ private FileAccessHelper&MockObject $fileAccessHelper;
+ private IAppManager&MockObject $appManager;
+ private ImageManager&MockObject $imageManager;
+ private IconController $iconController;
protected function setUp(): void {
$this->request = $this->createMock(IRequest::class);
@@ -67,6 +39,7 @@ class IconControllerTest extends TestCase {
$this->iconBuilder = $this->createMock(IconBuilder::class);
$this->imageManager = $this->createMock(ImageManager::class);
$this->fileAccessHelper = $this->createMock(FileAccessHelper::class);
+ $this->appManager = $this->createMock(IAppManager::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->timeFactory->expects($this->any())
@@ -81,7 +54,8 @@ class IconControllerTest extends TestCase {
$this->themingDefaults,
$this->iconBuilder,
$this->imageManager,
- $this->fileAccessHelper
+ $this->fileAccessHelper,
+ $this->appManager,
);
parent::setUp();
@@ -92,22 +66,24 @@ class IconControllerTest extends TestCase {
$icon->expects($this->any())->method('getContent')->willReturn($data);
$icon->expects($this->any())->method('getMimeType')->willReturn('image type');
$icon->expects($this->any())->method('getEtag')->willReturn('my etag');
+ $icon->expects($this->any())->method('getName')->willReturn('my name');
+ $icon->expects($this->any())->method('getMTime')->willReturn(42);
$icon->method('getName')->willReturn($filename);
return new SimpleFile($icon);
}
- public function testGetThemedIcon() {
+ public function testGetThemedIcon(): void {
$file = $this->iconFileMock('icon-core-filetypes_folder.svg', 'filecontent');
$this->imageManager->expects($this->once())
->method('getCachedImage')
->with('icon-core-filetypes_folder.svg')
->willReturn($file);
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
- $expected->cacheFor(86400);
+ $expected->cacheFor(86400, false, true);
$this->assertEquals($expected, $this->iconController->getThemedIcon('core', 'filetypes/folder.svg'));
}
- public function testGetFaviconDefault() {
+ public function testGetFaviconDefault(): void {
if (!extension_loaded('imagick')) {
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
}
@@ -119,13 +95,13 @@ class IconControllerTest extends TestCase {
$this->imageManager->expects($this->once())
->method('getImage', false)
->with('favicon')
- ->will($this->throwException(new NotFoundException()));
+ ->willThrowException(new NotFoundException());
$this->imageManager->expects($this->any())
->method('shouldReplaceIcons')
->willReturn(true);
$this->imageManager->expects($this->once())
->method('getCachedImage')
- ->will($this->throwException(new NotFoundException()));
+ ->willThrowException(new NotFoundException());
$this->iconBuilder->expects($this->once())
->method('getFavicon')
->with('core')
@@ -139,11 +115,11 @@ class IconControllerTest extends TestCase {
$this->assertEquals($expected, $this->iconController->getFavicon());
}
- public function testGetFaviconFail() {
+ public function testGetFaviconFail(): void {
$this->imageManager->expects($this->once())
->method('getImage')
->with('favicon', false)
- ->will($this->throwException(new NotFoundException()));
+ ->willThrowException(new NotFoundException());
$this->imageManager->expects($this->any())
->method('shouldReplaceIcons')
->willReturn(false);
@@ -157,7 +133,7 @@ class IconControllerTest extends TestCase {
$this->assertEquals($expected, $this->iconController->getFavicon());
}
- public function testGetTouchIconDefault() {
+ public function testGetTouchIconDefault(): void {
if (!extension_loaded('imagick')) {
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
}
@@ -168,7 +144,7 @@ class IconControllerTest extends TestCase {
$this->imageManager->expects($this->once())
->method('getImage')
- ->will($this->throwException(new NotFoundException()));
+ ->willThrowException(new NotFoundException());
$this->imageManager->expects($this->any())
->method('shouldReplaceIcons')
->willReturn(true);
@@ -179,7 +155,7 @@ class IconControllerTest extends TestCase {
$file = $this->iconFileMock('filename', 'filecontent');
$this->imageManager->expects($this->once())
->method('getCachedImage')
- ->will($this->throwException(new NotFoundException()));
+ ->willThrowException(new NotFoundException());
$this->imageManager->expects($this->once())
->method('setCachedImage')
->willReturn($file);
@@ -189,11 +165,11 @@ class IconControllerTest extends TestCase {
$this->assertEquals($expected, $this->iconController->getTouchIcon());
}
- public function testGetTouchIconFail() {
+ public function testGetTouchIconFail(): void {
$this->imageManager->expects($this->once())
->method('getImage')
->with('favicon')
- ->will($this->throwException(new NotFoundException()));
+ ->willThrowException(new NotFoundException());
$this->imageManager->expects($this->any())
->method('shouldReplaceIcons')
->willReturn(false);
diff --git a/apps/theming/tests/Controller/ThemingControllerTest.php b/apps/theming/tests/Controller/ThemingControllerTest.php
index c5ecc08b211..fb461f03a28 100644
--- a/apps/theming/tests/Controller/ThemingControllerTest.php
+++ b/apps/theming/tests/Controller/ThemingControllerTest.php
@@ -1,94 +1,64 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
- * @author Julius Haertl <jus@bitgrid.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Kyle Fazzari <kyrofa@ubuntu.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Michael Weimann <mail@michael-weimann.eu>
- * @author rakekniven <mark.ziegler@rakekniven.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests\Controller;
use OC\L10N\L10N;
-use OC\Template\SCSSCacher;
use OCA\Theming\Controller\ThemingController;
use OCA\Theming\ImageManager;
+use OCA\Theming\Service\ThemesService;
use OCA\Theming\ThemingDefaults;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\FileDisplayResponse;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Http\NotFoundResponse;
+use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IConfig;
use OCP\IL10N;
+use OCP\INavigationManager;
use OCP\IRequest;
use OCP\ITempManager;
use OCP\IURLGenerator;
+use OCP\Server;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class ThemingControllerTest extends TestCase {
- /** @var IRequest|MockObject */
- private $request;
- /** @var IConfig|MockObject */
- private $config;
- /** @var ThemingDefaults|MockObject */
- private $themingDefaults;
- /** @var IL10N|MockObject */
- private $l10n;
- /** @var ThemingController */
- private $themingController;
- /** @var ITempManager */
- private $tempManager;
- /** @var IAppManager|MockObject */
- private $appManager;
- /** @var IAppData|MockObject */
- private $appData;
- /** @var ImageManager|MockObject */
- private $imageManager;
- /** @var SCSSCacher */
- private $scssCacher;
- /** @var IURLGenerator */
- private $urlGenerator;
+
+ private IRequest&MockObject $request;
+ private IConfig&MockObject $config;
+ private IAppConfig&MockObject $appConfig;
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IL10N&MockObject $l10n;
+ private IAppManager&MockObject $appManager;
+ private ImageManager&MockObject $imageManager;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ThemesService&MockObject $themesService;
+ private INavigationManager&MockObject $navigationManager;
+
+ private ThemingController $themingController;
protected function setUp(): void {
$this->request = $this->createMock(IRequest::class);
$this->config = $this->createMock(IConfig::class);
+ $this->appConfig = $this->createMock(IAppConfig::class);
$this->themingDefaults = $this->createMock(ThemingDefaults::class);
$this->l10n = $this->createMock(L10N::class);
- $this->appData = $this->createMock(IAppData::class);
$this->appManager = $this->createMock(IAppManager::class);
- $this->tempManager = \OC::$server->getTempManager();
- $this->scssCacher = $this->createMock(SCSSCacher::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->imageManager = $this->createMock(ImageManager::class);
+ $this->themesService = $this->createMock(ThemesService::class);
+ $this->navigationManager = $this->createMock(INavigationManager::class);
$timeFactory = $this->createMock(ITimeFactory::class);
$timeFactory->expects($this->any())
@@ -101,20 +71,20 @@ class ThemingControllerTest extends TestCase {
'theming',
$this->request,
$this->config,
+ $this->appConfig,
$this->themingDefaults,
$this->l10n,
- $this->tempManager,
- $this->appData,
- $this->scssCacher,
$this->urlGenerator,
$this->appManager,
- $this->imageManager
+ $this->imageManager,
+ $this->themesService,
+ $this->navigationManager,
);
parent::setUp();
}
- public function dataUpdateStylesheetSuccess() {
+ public static function dataUpdateStylesheetSuccess(): array {
return [
['name', str_repeat('a', 250), 'Saved'],
['url', 'https://nextcloud.com/' . str_repeat('a', 478), 'Saved'],
@@ -127,14 +97,8 @@ class ThemingControllerTest extends TestCase {
];
}
- /**
- * @dataProvider dataUpdateStylesheetSuccess
- *
- * @param string $setting
- * @param string $value
- * @param string $message
- */
- public function testUpdateStylesheetSuccess($setting, $value, $message) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateStylesheetSuccess')]
+ public function testUpdateStylesheetSuccess(string $setting, string $value, string $message): void {
$this->themingDefaults
->expects($this->once())
->method('set')
@@ -145,23 +109,12 @@ class ThemingControllerTest extends TestCase {
->willReturnCallback(function ($str) {
return $str;
});
- $this->scssCacher
- ->expects($this->once())
- ->method('getCachedSCSS')
- ->with('core', '/core/css/css-variables.scss')
- ->willReturn('/core/css/someHash-css-variables.scss');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkTo')
- ->with('', '/core/css/someHash-css-variables.scss')
- ->willReturn('/nextcloudWebroot/core/css/someHash-css-variables.scss');
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => $message,
- 'serverCssUrl' => '/nextcloudWebroot/core/css/someHash-css-variables.scss',
],
'status' => 'success',
]
@@ -169,31 +122,39 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->updateStylesheet($setting, $value));
}
- public function dataUpdateStylesheetError() {
+ public static function dataUpdateStylesheetError(): array {
+ $urls = [
+ 'url' => 'web address',
+ 'imprintUrl' => 'legal notice address',
+ 'privacyUrl' => 'privacy policy address',
+ ];
+
+ $urlTests = [];
+ foreach ($urls as $urlKey => $urlName) {
+ // Check length limit
+ $urlTests[] = [$urlKey, 'http://example.com/' . str_repeat('a', 501), "The given {$urlName} is too long"];
+ // Check potential evil javascript
+ $urlTests[] = [$urlKey, 'javascript:alert(1)', "The given {$urlName} is not a valid URL"];
+ // Check XSS
+ $urlTests[] = [$urlKey, 'https://example.com/"><script/src="alert(\'1\')"><a/href/="', "The given {$urlName} is not a valid URL"];
+ }
+
return [
['name', str_repeat('a', 251), 'The given name is too long'],
- ['url', 'http://example.com/' . str_repeat('a', 501), 'The given web address is too long'],
- ['url', str_repeat('a', 501), 'The given web address is not a valid URL'],
- ['url', 'javascript:alert(1)', 'The given web address is not a valid URL'],
['slogan', str_repeat('a', 501), 'The given slogan is too long'],
- ['color', '0082C9', 'The given color is invalid'],
- ['color', '#0082Z9', 'The given color is invalid'],
- ['color', 'Nextcloud', 'The given color is invalid'],
- ['imprintUrl', '0082C9', 'The given legal notice address is not a valid URL'],
- ['imprintUrl', '0082C9', 'The given legal notice address is not a valid URL'],
- ['imprintUrl', 'javascript:foo', 'The given legal notice address is not a valid URL'],
- ['privacyUrl', '#0082Z9', 'The given privacy policy address is not a valid URL'],
+ ['primary_color', '0082C9', 'The given color is invalid'],
+ ['primary_color', '#0082Z9', 'The given color is invalid'],
+ ['primary_color', 'Nextcloud', 'The given color is invalid'],
+ ['background_color', '0082C9', 'The given color is invalid'],
+ ['background_color', '#0082Z9', 'The given color is invalid'],
+ ['background_color', 'Nextcloud', 'The given color is invalid'],
+
+ ...$urlTests,
];
}
- /**
- * @dataProvider dataUpdateStylesheetError
- *
- * @param string $setting
- * @param string $value
- * @param string $message
- */
- public function testUpdateStylesheetError($setting, $value, $message) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateStylesheetError')]
+ public function testUpdateStylesheetError(string $setting, string $value, string $message): void {
$this->themingDefaults
->expects($this->never())
->method('set')
@@ -207,8 +168,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => $message,
],
'status' => 'error',
@@ -218,14 +179,14 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->updateStylesheet($setting, $value));
}
- public function testUpdateLogoNoData() {
+ public function testUpdateLogoNoData(): void {
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('logo');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn(null);
@@ -238,8 +199,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => 'No file uploaded',
],
'status' => 'failure',
@@ -250,29 +211,56 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
+ public function testUploadInvalidUploadKey(): void {
+ $this->request
+ ->expects($this->once())
+ ->method('getParam')
+ ->with('key')
+ ->willReturn('invalid');
+ $this->request
+ ->expects($this->never())
+ ->method('getUploadedFile');
+ $this->l10n
+ ->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($str) {
+ return $str;
+ });
+
+ $expected = new DataResponse(
+ [
+ 'data'
+ => [
+ 'message' => 'Invalid key',
+ ],
+ 'status' => 'failure',
+ ],
+ Http::STATUS_BAD_REQUEST
+ );
+
+ $this->assertEquals($expected, $this->themingController->uploadImage());
+ }
+
/**
* Checks that trying to upload an SVG favicon without imagemagick
* results in an unsupported media type response.
- *
- * @test
- * @return void
*/
- public function testUploadSVGFaviconWithoutImagemagick() {
+ public function testUploadSVGFaviconWithoutImagemagick(): void {
$this->imageManager
->method('shouldReplaceIcons')
->willReturn(false);
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('favicon');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
- 'tmp_name' => __DIR__ . '/../../../../tests/data/testimagelarge.svg',
+ 'tmp_name' => __DIR__ . '/../../../../tests/data/testimagelarge.svg',
'type' => 'image/svg',
'name' => 'testimagelarge.svg',
'error' => 0,
@@ -290,8 +278,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => 'Unsupported image type',
],
'status' => 'failure'
@@ -302,18 +290,18 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- public function testUpdateLogoInvalidMimeType() {
+ public function testUpdateLogoInvalidMimeType(): void {
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('logo');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
- 'tmp_name' => __DIR__ . '/../../../../tests/data/lorem.txt',
+ 'tmp_name' => __DIR__ . '/../../../../tests/data/lorem.txt',
'type' => 'application/pdf',
'name' => 'logo.pdf',
'error' => 0,
@@ -331,8 +319,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => 'Unsupported image type',
],
'status' => 'failure'
@@ -343,7 +331,7 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- public function dataUpdateImages() {
+ public static function dataUpdateImages(): array {
return [
['image/jpeg', false],
['image/jpeg', true],
@@ -354,20 +342,20 @@ class ThemingControllerTest extends TestCase {
];
}
- /** @dataProvider dataUpdateImages */
- public function testUpdateLogoNormalLogoUpload($mimeType, $folderExists = true) {
- $tmpLogo = \OC::$server->getTempManager()->getTemporaryFolder() . '/logo.svg';
- $destination = \OC::$server->getTempManager()->getTemporaryFolder();
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateImages')]
+ public function testUpdateLogoNormalLogoUpload(string $mimeType, bool $folderExists = true): void {
+ $tmpLogo = Server::get(ITempManager::class)->getTemporaryFolder() . '/logo.svg';
+ $destination = Server::get(ITempManager::class)->getTemporaryFolder();
touch($tmpLogo);
copy(__DIR__ . '/../../../../tests/data/testimage.png', $tmpLogo);
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('logo');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
@@ -383,9 +371,6 @@ class ThemingControllerTest extends TestCase {
return $str;
});
- $this->urlGenerator->expects($this->once())
- ->method('linkTo')
- ->willReturn('serverCss');
$this->imageManager->expects($this->once())
->method('getImageUrl')
->with('logo')
@@ -396,12 +381,11 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'name' => 'logo.svg',
'message' => 'Saved',
'url' => 'imageUrl',
- 'serverCssUrl' => 'serverCss'
],
'status' => 'success'
]
@@ -410,19 +394,18 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- /** @dataProvider dataUpdateImages */
- public function testUpdateLogoLoginScreenUpload($folderExists) {
- $tmpLogo = \OC::$server->getTempManager()->getTemporaryFolder() . 'logo.png';
+ public function testUpdateLogoLoginScreenUpload(): void {
+ $tmpLogo = Server::get(ITempManager::class)->getTemporaryFolder() . 'logo.png';
touch($tmpLogo);
copy(__DIR__ . '/../../../../tests/data/desktopapp.png', $tmpLogo);
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('background');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
@@ -441,21 +424,17 @@ class ThemingControllerTest extends TestCase {
$this->imageManager->expects($this->once())
->method('updateImage');
- $this->urlGenerator->expects($this->once())
- ->method('linkTo')
- ->willReturn('serverCss');
$this->imageManager->expects($this->once())
->method('getImageUrl')
->with('background')
->willReturn('imageUrl');
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'name' => 'logo.svg',
'message' => 'Saved',
'url' => 'imageUrl',
- 'serverCssUrl' => 'serverCss'
],
'status' => 'success'
]
@@ -463,18 +442,18 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- public function testUpdateLogoLoginScreenUploadWithInvalidImage() {
- $tmpLogo = \OC::$server->getTempManager()->getTemporaryFolder() . '/logo.svg';
+ public function testUpdateLogoLoginScreenUploadWithInvalidImage(): void {
+ $tmpLogo = Server::get(ITempManager::class)->getTemporaryFolder() . '/logo.svg';
touch($tmpLogo);
- file_put_contents($tmpLogo, file_get_contents(__DIR__ . '/../../../../tests/data/data.zip'));
+ file_put_contents($tmpLogo, file_get_contents(__DIR__ . '/../../../../tests/data/data.zip'));
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('logo');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
@@ -496,8 +475,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => 'Unsupported image type',
],
'status' => 'failure'
@@ -507,7 +486,7 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- public function dataPhpUploadErrors() {
+ public static function dataPhpUploadErrors(): array {
return [
[UPLOAD_ERR_INI_SIZE, 'The uploaded file exceeds the upload_max_filesize directive in php.ini'],
[UPLOAD_ERR_FORM_SIZE, 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'],
@@ -519,17 +498,15 @@ class ThemingControllerTest extends TestCase {
];
}
- /**
- * @dataProvider dataPhpUploadErrors
- */
- public function testUpdateLogoLoginScreenUploadWithInvalidImageUpload($error, $expectedErrorMessage) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataPhpUploadErrors')]
+ public function testUpdateLogoLoginScreenUploadWithInvalidImageUpload(int $error, string $expectedErrorMessage): void {
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('background');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
@@ -547,8 +524,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => $expectedErrorMessage,
],
'status' => 'failure'
@@ -558,17 +535,15 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- /**
- * @dataProvider dataPhpUploadErrors
- */
- public function testUpdateLogoUploadWithInvalidImageUpload($error, $expectedErrorMessage) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataPhpUploadErrors')]
+ public function testUpdateLogoUploadWithInvalidImageUpload($error, $expectedErrorMessage): void {
$this->request
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getParam')
->with('key')
->willReturn('background');
$this->request
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getUploadedFile')
->with('image')
->willReturn([
@@ -586,8 +561,8 @@ class ThemingControllerTest extends TestCase {
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'message' => $expectedErrorMessage
],
'status' => 'failure'
@@ -597,7 +572,7 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->uploadImage());
}
- public function testUndo() {
+ public function testUndo(): void {
$this->l10n
->expects($this->once())
->method('t')
@@ -608,24 +583,13 @@ class ThemingControllerTest extends TestCase {
->method('undo')
->with('MySetting')
->willReturn('MyValue');
- $this->scssCacher
- ->expects($this->once())
- ->method('getCachedSCSS')
- ->with('core', '/core/css/css-variables.scss')
- ->willReturn('/core/css/someHash-css-variables.scss');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkTo')
- ->with('', '/core/css/someHash-css-variables.scss')
- ->willReturn('/nextcloudWebroot/core/css/someHash-css-variables.scss');
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'value' => 'MyValue',
- 'message' => 'Saved',
- 'serverCssUrl' => '/nextcloudWebroot/core/css/someHash-css-variables.scss',
+ 'message' => 'Saved'
],
'status' => 'success'
]
@@ -633,15 +597,15 @@ class ThemingControllerTest extends TestCase {
$this->assertEquals($expected, $this->themingController->undo('MySetting'));
}
- public function dataUndoDelete() {
+ public static function dataUndoDelete(): array {
return [
[ 'backgroundMime', 'background' ],
[ 'logoMime', 'logo' ]
];
}
- /** @dataProvider dataUndoDelete */
- public function testUndoDelete($value, $filename) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataUndoDelete')]
+ public function testUndoDelete(string $value, string $filename): void {
$this->l10n
->expects($this->once())
->method('t')
@@ -652,24 +616,13 @@ class ThemingControllerTest extends TestCase {
->method('undo')
->with($value)
->willReturn($value);
- $this->scssCacher
- ->expects($this->once())
- ->method('getCachedSCSS')
- ->with('core', '/core/css/css-variables.scss')
- ->willReturn('/core/css/someHash-css-variables.scss');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkTo')
- ->with('', '/core/css/someHash-css-variables.scss')
- ->willReturn('/nextcloudWebroot/core/css/someHash-css-variables.scss');
$expected = new DataResponse(
[
- 'data' =>
- [
+ 'data'
+ => [
'value' => $value,
'message' => 'Saved',
- 'serverCssUrl' => '/nextcloudWebroot/core/css/someHash-css-variables.scss',
],
'status' => 'success'
]
@@ -679,17 +632,19 @@ class ThemingControllerTest extends TestCase {
- public function testGetLogoNotExistent() {
+ public function testGetLogoNotExistent(): void {
$this->imageManager->method('getImage')
->with($this->equalTo('logo'))
->willThrowException(new NotFoundException());
- $expected = new Http\NotFoundResponse();
+ $expected = new NotFoundResponse();
$this->assertEquals($expected, $this->themingController->getImage('logo'));
}
- public function testGetLogo() {
+ public function testGetLogo(): void {
$file = $this->createMock(ISimpleFile::class);
+ $file->method('getName')->willReturn('logo.svg');
+ $file->method('getMTime')->willReturn(42);
$this->imageManager->expects($this->once())
->method('getImage')
->willReturn($file);
@@ -699,27 +654,29 @@ class ThemingControllerTest extends TestCase {
->with('theming', 'logoMime', '')
->willReturn('text/svg');
- @$expected = new Http\FileDisplayResponse($file);
+ @$expected = new FileDisplayResponse($file);
$expected->cacheFor(3600);
$expected->addHeader('Content-Type', 'text/svg');
$expected->addHeader('Content-Disposition', 'attachment; filename="logo"');
- $csp = new Http\ContentSecurityPolicy();
+ $csp = new ContentSecurityPolicy();
$csp->allowInlineStyle();
$expected->setContentSecurityPolicy($csp);
@$this->assertEquals($expected, $this->themingController->getImage('logo'));
}
- public function testGetLoginBackgroundNotExistent() {
+ public function testGetLoginBackgroundNotExistent(): void {
$this->imageManager->method('getImage')
->with($this->equalTo('background'))
->willThrowException(new NotFoundException());
- $expected = new Http\NotFoundResponse();
+ $expected = new NotFoundResponse();
$this->assertEquals($expected, $this->themingController->getImage('background'));
}
- public function testGetLoginBackground() {
+ public function testGetLoginBackground(): void {
$file = $this->createMock(ISimpleFile::class);
+ $file->method('getName')->willReturn('background.png');
+ $file->method('getMTime')->willReturn(42);
$this->imageManager->expects($this->once())
->method('getImage')
->willReturn($file);
@@ -730,61 +687,25 @@ class ThemingControllerTest extends TestCase {
->with('theming', 'backgroundMime', '')
->willReturn('image/png');
- @$expected = new Http\FileDisplayResponse($file);
+ @$expected = new FileDisplayResponse($file);
$expected->cacheFor(3600);
$expected->addHeader('Content-Type', 'image/png');
$expected->addHeader('Content-Disposition', 'attachment; filename="background"');
- $csp = new Http\ContentSecurityPolicy();
+ $csp = new ContentSecurityPolicy();
$csp->allowInlineStyle();
$expected->setContentSecurityPolicy($csp);
@$this->assertEquals($expected, $this->themingController->getImage('background'));
}
-
- public function testGetStylesheet() {
- $this->appManager->expects($this->once())->method('getAppPath')->with('theming')->willReturn(\OC::$SERVERROOT . '/theming');
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->any())->method('getName')->willReturn('theming.css');
- $file->expects($this->any())->method('getContent')->willReturn('compiled');
- $this->scssCacher->expects($this->once())->method('process')->willReturn(true);
- $this->scssCacher->expects($this->once())->method('getCachedCSS')->willReturn($file);
-
- $response = new Http\FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']);
- $response->cacheFor(86400);
-
- $actual = $this->themingController->getStylesheet();
- $this->assertEquals($response, $actual);
- }
-
- public function testGetStylesheetFails() {
- $this->appManager->expects($this->once())->method('getAppPath')->with('theming')->willReturn(\OC::$SERVERROOT . '/theming');
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->any())->method('getName')->willReturn('theming.css');
- $file->expects($this->any())->method('getContent')->willReturn('compiled');
- $this->scssCacher->expects($this->once())->method('process')->willReturn(true);
- $this->scssCacher->expects($this->once())->method('getCachedCSS')->willThrowException(new NotFoundException());
- $response = new Http\NotFoundResponse();
-
- $actual = $this->themingController->getStylesheet();
- $this->assertEquals($response, $actual);
- }
-
- public function testGetStylesheetOutsideServerroot() {
- $this->appManager->expects($this->once())->method('getAppPath')->with('theming')->willReturn('/outside/serverroot/theming');
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->any())->method('getName')->willReturn('theming.css');
- $file->expects($this->any())->method('getContent')->willReturn('compiled');
- $this->scssCacher->expects($this->once())->method('process')->with('/outside/serverroot/theming', 'css/theming.scss', 'theming')->willReturn(true);
- $this->scssCacher->expects($this->once())->method('getCachedCSS')->willReturn($file);
-
- $response = new Http\FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']);
- $response->cacheFor(86400);
-
- $actual = $this->themingController->getStylesheet();
- $this->assertEquals($response, $actual);
+ public static function dataGetManifest(): array {
+ return [
+ [true],
+ [false],
+ ];
}
- public function testGetManifest() {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetManifest')]
+ public function testGetManifest(bool $standalone): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -795,24 +716,26 @@ class ThemingControllerTest extends TestCase {
->method('getName')
->willReturn('Nextcloud');
$this->urlGenerator
- ->expects($this->at(0))
+ ->expects($this->once())
->method('getBaseUrl')
->willReturn('localhost');
$this->urlGenerator
- ->expects($this->at(1))
- ->method('linkToRoute')
- ->with('theming.Icon.getTouchIcon', ['app' => 'core'])
- ->willReturn('touchicon');
- $this->urlGenerator
- ->expects($this->at(2))
+ ->expects($this->exactly(2))
->method('linkToRoute')
- ->with('theming.Icon.getFavicon', ['app' => 'core'])
- ->willReturn('favicon');
- $response = new Http\JSONResponse([
+ ->willReturnMap([
+ ['theming.Icon.getTouchIcon', ['app' => 'core'], 'touchicon'],
+ ['theming.Icon.getFavicon', ['app' => 'core'], 'favicon'],
+ ]);
+ $this->config
+ ->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->with('theming.standalone_window.enabled', true)
+ ->willReturn($standalone);
+ $response = new JSONResponse([
'name' => 'Nextcloud',
'start_url' => 'localhost',
- 'icons' =>
- [
+ 'icons'
+ => [
[
'src' => 'touchicon?v=0',
'type' => 'image/png',
@@ -824,7 +747,12 @@ class ThemingControllerTest extends TestCase {
'sizes' => '16x16'
]
],
- 'display' => 'standalone'
+ 'display_override' => [$standalone ? 'minimal-ui' : ''],
+ 'display' => $standalone ? 'standalone' : 'browser',
+ 'short_name' => 'Nextcloud',
+ 'theme_color' => null,
+ 'background_color' => null,
+ 'description' => null
]);
$response->cacheFor(3600);
$this->assertEquals($response, $this->themingController->getManifest('core'));
diff --git a/apps/theming/tests/Controller/UserThemeControllerTest.php b/apps/theming/tests/Controller/UserThemeControllerTest.php
new file mode 100644
index 00000000000..9a8c1cd19aa
--- /dev/null
+++ b/apps/theming/tests/Controller/UserThemeControllerTest.php
@@ -0,0 +1,124 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Controller;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\Controller\UserThemeController;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\BackgroundService;
+use OCA\Theming\Service\ThemesService;
+use OCA\Theming\Themes\DarkHighContrastTheme;
+use OCA\Theming\Themes\DarkTheme;
+use OCA\Theming\Themes\DefaultTheme;
+use OCA\Theming\Themes\DyslexiaFont;
+use OCA\Theming\Themes\HighContrastTheme;
+use OCA\Theming\Themes\LightTheme;
+use OCA\Theming\ThemingDefaults;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\OCS\OCSBadRequestException;
+use OCP\IConfig;
+use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class UserThemeControllerTest extends TestCase {
+ private IRequest&MockObject $request;
+ private IConfig&MockObject $config;
+ private IUserSession&MockObject $userSession;
+ private ThemesService&MockObject $themesService;
+ private ThemingDefaults&MockObject $themingDefaults;
+ private BackgroundService&MockObject $backgroundService;
+ private UserThemeController $userThemeController;
+
+
+ /** @var ITheme[] */
+ private array $themes;
+
+ protected function setUp(): void {
+ $this->request = $this->createMock(IRequest::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->themesService = $this->createMock(ThemesService::class);
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->backgroundService = $this->createMock(BackgroundService::class);
+
+ $this->themes = [
+ 'default' => $this->createMock(DefaultTheme::class),
+ 'light' => $this->createMock(LightTheme::class),
+ 'dark' => $this->createMock(DarkTheme::class),
+ 'light-highcontrast' => $this->createMock(HighContrastTheme::class),
+ 'dark-highcontrast' => $this->createMock(DarkHighContrastTheme::class),
+ 'opendyslexic' => $this->createMock(DyslexiaFont::class),
+ ];
+
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+ $this->userThemeController = new UserThemeController(
+ Application::APP_ID,
+ $this->request,
+ $this->config,
+ $this->userSession,
+ $this->themesService,
+ $this->themingDefaults,
+ $this->backgroundService,
+ );
+
+ parent::setUp();
+ }
+
+ public static function dataTestThemes(): array {
+ return [
+ ['default'],
+ ['light'],
+ ['dark'],
+ ['light-highcontrast'],
+ ['dark-highcontrast'],
+ ['opendyslexic'],
+ ['', OCSBadRequestException::class],
+ ['badTheme', OCSBadRequestException::class],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestThemes')]
+ public function testEnableTheme(string $themeId, ?string $exception = null): void {
+ $this->themesService
+ ->expects($this->any())
+ ->method('getThemes')
+ ->willReturn($this->themes);
+
+ if ($exception) {
+ $this->expectException($exception);
+ }
+
+ $expected = new DataResponse();
+ $this->assertEquals($expected, $this->userThemeController->enableTheme($themeId));
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestThemes')]
+ public function testDisableTheme(string $themeId, ?string $exception = null): void {
+ $this->themesService
+ ->expects($this->any())
+ ->method('getThemes')
+ ->willReturn($this->themes);
+
+ if ($exception) {
+ $this->expectException($exception);
+ }
+
+ $expected = new DataResponse();
+ $this->assertEquals($expected, $this->userThemeController->disableTheme($themeId));
+ }
+}
diff --git a/apps/theming/tests/IconBuilderTest.php b/apps/theming/tests/IconBuilderTest.php
index 542bfcc51bd..d881e4eb75c 100644
--- a/apps/theming/tests/IconBuilderTest.php
+++ b/apps/theming/tests/IconBuilderTest.php
@@ -1,31 +1,10 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Haertl <jus@bitgrid.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests;
use OC\Files\AppData\AppData;
@@ -36,25 +15,18 @@ use OCA\Theming\Util;
use OCP\App\IAppManager;
use OCP\Files\NotFoundException;
use OCP\IConfig;
-use PHPUnit\Framework\Error\Warning;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class IconBuilderTest extends TestCase {
-
- /** @var IConfig */
- protected $config;
- /** @var AppData */
- protected $appData;
- /** @var ThemingDefaults */
- protected $themingDefaults;
- /** @var Util */
- protected $util;
- /** @var ImageManager */
- protected $imageManager;
- /** @var IconBuilder */
- protected $iconBuilder;
- /** @var IAppManager */
- protected $appManager;
+ protected IConfig&MockObject $config;
+ protected AppData&MockObject $appData;
+ protected ThemingDefaults&MockObject $themingDefaults;
+ protected ImageManager&MockObject $imageManager;
+ protected IAppManager&MockObject $appManager;
+ protected Util $util;
+ protected IconBuilder $iconBuilder;
protected function setUp(): void {
parent::setUp();
@@ -64,7 +36,7 @@ class IconBuilderTest extends TestCase {
$this->themingDefaults = $this->createMock(ThemingDefaults::class);
$this->appManager = $this->createMock(IAppManager::class);
$this->imageManager = $this->createMock(ImageManager::class);
- $this->util = new Util($this->config, $this->appManager, $this->appData);
+ $this->util = new Util($this->createMock(ServerVersion::class), $this->config, $this->appManager, $this->appData, $this->imageManager);
$this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util, $this->imageManager);
}
@@ -81,7 +53,7 @@ class IconBuilderTest extends TestCase {
}
}
- public function dataRenderAppIcon() {
+ public static function dataRenderAppIcon(): array {
return [
['core', '#0082c9', 'touch-original.png'],
['core', '#FF0000', 'touch-core-red.png'],
@@ -91,23 +63,18 @@ class IconBuilderTest extends TestCase {
];
}
- /**
- * @dataProvider dataRenderAppIcon
- * @param $app
- * @param $color
- * @param $file
- */
- public function testRenderAppIcon($app, $color, $file) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataRenderAppIcon')]
+ public function testRenderAppIcon(string $app, string $color, string $file): void {
$this->checkImagick();
$this->themingDefaults->expects($this->once())
->method('getColorPrimary')
->willReturn($color);
$this->appData->expects($this->once())
->method('getFolder')
- ->with('images')
+ ->with('global/images')
->willThrowException(new NotFoundException());
- $expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/data/" . $file);
+ $expectedIcon = new \Imagick(realpath(__DIR__) . '/data/' . $file);
$icon = $this->iconBuilder->renderAppIcon($app, 512);
$this->assertEquals(true, $icon->valid());
@@ -120,23 +87,18 @@ class IconBuilderTest extends TestCase {
// cloud be something like $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1])
}
- /**
- * @dataProvider dataRenderAppIcon
- * @param $app
- * @param $color
- * @param $file
- */
- public function testGetTouchIcon($app, $color, $file) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataRenderAppIcon')]
+ public function testGetTouchIcon(string $app, string $color, string $file): void {
$this->checkImagick();
$this->themingDefaults->expects($this->once())
->method('getColorPrimary')
->willReturn($color);
$this->appData->expects($this->once())
->method('getFolder')
- ->with('images')
+ ->with('global/images')
->willThrowException(new NotFoundException());
- $expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/data/" . $file);
+ $expectedIcon = new \Imagick(realpath(__DIR__) . '/data/' . $file);
$icon = new \Imagick();
$icon->readImageBlob($this->iconBuilder->getTouchIcon($app));
@@ -150,13 +112,8 @@ class IconBuilderTest extends TestCase {
// cloud be something like $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1])
}
- /**
- * @dataProvider dataRenderAppIcon
- * @param $app
- * @param $color
- * @param $file
- */
- public function testGetFavicon($app, $color, $file) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataRenderAppIcon')]
+ public function testGetFavicon(string $app, string $color, string $file): void {
$this->checkImagick();
$this->imageManager->expects($this->once())
->method('shouldReplaceIcons')
@@ -166,10 +123,10 @@ class IconBuilderTest extends TestCase {
->willReturn($color);
$this->appData->expects($this->once())
->method('getFolder')
- ->with('images')
+ ->with('global/images')
->willThrowException(new NotFoundException());
- $expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/data/" . $file);
+ $expectedIcon = new \Imagick(realpath(__DIR__) . '/data/' . $file);
$actualIcon = $this->iconBuilder->getFavicon($app);
$icon = new \Imagick();
@@ -185,10 +142,9 @@ class IconBuilderTest extends TestCase {
// cloud be something like $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1])
}
- public function testGetFaviconNotFound() {
+ public function testGetFaviconNotFound(): void {
$this->checkImagick();
- $this->expectException(Warning::class);
- $util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
+ $util = $this->createMock(Util::class);
$iconBuilder = new IconBuilder($this->themingDefaults, $util, $this->imageManager);
$this->imageManager->expects($this->once())
->method('shouldReplaceIcons')
@@ -199,10 +155,9 @@ class IconBuilderTest extends TestCase {
$this->assertFalse($iconBuilder->getFavicon('noapp'));
}
- public function testGetTouchIconNotFound() {
+ public function testGetTouchIconNotFound(): void {
$this->checkImagick();
- $this->expectException(Warning::class);
- $util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
+ $util = $this->createMock(Util::class);
$iconBuilder = new IconBuilder($this->themingDefaults, $util, $this->imageManager);
$util->expects($this->once())
->method('getAppIcon')
@@ -210,14 +165,13 @@ class IconBuilderTest extends TestCase {
$this->assertFalse($iconBuilder->getTouchIcon('noapp'));
}
- public function testColorSvgNotFound() {
+ public function testColorSvgNotFound(): void {
$this->checkImagick();
- $this->expectException(Warning::class);
- $util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
+ $util = $this->createMock(Util::class);
$iconBuilder = new IconBuilder($this->themingDefaults, $util, $this->imageManager);
$util->expects($this->once())
->method('getAppImage')
->willReturn('notexistingfile');
- $this->assertFalse($iconBuilder->colorSvg('noapp','noimage'));
+ $this->assertFalse($iconBuilder->colorSvg('noapp', 'noimage'));
}
}
diff --git a/apps/theming/tests/ImageManagerTest.php b/apps/theming/tests/ImageManagerTest.php
index 08a8fe821f0..0c4d555cc00 100644
--- a/apps/theming/tests/ImageManagerTest.php
+++ b/apps/theming/tests/ImageManagerTest.php
@@ -1,62 +1,35 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Julius Haertl <jus@bitgrid.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Michael Weimann <mail@michael-weimann.eu>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests;
use OCA\Theming\ImageManager;
+use OCA\Theming\Service\BackgroundService;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\ICacheFactory;
use OCP\IConfig;
-use OCP\ILogger;
use OCP\ITempManager;
use OCP\IURLGenerator;
use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
use Test\TestCase;
class ImageManagerTest extends TestCase {
-
- /** @var IConfig|MockObject */
- protected $config;
- /** @var IAppData|MockObject */
- protected $appData;
- /** @var ImageManager */
- protected $imageManager;
- /** @var IURLGenerator|MockObject */
- private $urlGenerator;
- /** @var ICacheFactory|MockObject */
- private $cacheFactory;
- /** @var ILogger|MockObject */
- private $logger;
- /** @var ITempManager|MockObject */
- private $tempManager;
+ protected IConfig&MockObject $config;
+ protected IAppData&MockObject $appData;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ICacheFactory&MockObject $cacheFactory;
+ private LoggerInterface&MockObject $logger;
+ private ITempManager&MockObject $tempManager;
+ private ISimpleFolder&MockObject $rootFolder;
+ protected ImageManager $imageManager;
protected function setUp(): void {
parent::setUp();
@@ -64,16 +37,24 @@ class ImageManagerTest extends TestCase {
$this->appData = $this->createMock(IAppData::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->logger = $this->createMock(ILogger::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
$this->tempManager = $this->createMock(ITempManager::class);
+ $this->rootFolder = $this->createMock(ISimpleFolder::class);
+ $backgroundService = $this->createMock(BackgroundService::class);
$this->imageManager = new ImageManager(
$this->config,
$this->appData,
$this->urlGenerator,
$this->cacheFactory,
$this->logger,
- $this->tempManager
+ $this->tempManager,
+ $backgroundService,
);
+ $this->appData
+ ->expects($this->any())
+ ->method('getFolder')
+ ->with('global')
+ ->willReturn($this->rootFolder);
}
private function checkImagick() {
@@ -101,57 +82,51 @@ class ImageManagerTest extends TestCase {
$file->expects($this->once())
->method('getContent')
->willReturn(file_get_contents(__DIR__ . '/../../../tests/data/testimage.png'));
- $folder->expects($this->at(0))
- ->method('fileExists')
- ->with('logo')
- ->willReturn(true);
- $folder->expects($this->at(1))
+ $folder->expects($this->exactly(2))
->method('fileExists')
- ->with('logo.png')
- ->willReturn(false);
- $folder->expects($this->at(2))
+ ->willReturnMap([
+ ['logo', true],
+ ['logo.png', false],
+ ]);
+ $folder->expects($this->once())
->method('getFile')
->with('logo')
->willReturn($file);
$newFile = $this->createMock(ISimpleFile::class);
- $folder->expects($this->at(3))
+ $folder->expects($this->once())
->method('newFile')
->with('logo.png')
->willReturn($newFile);
$newFile->expects($this->once())
->method('putContent');
- $this->appData->expects($this->once())
+ $this->rootFolder->expects($this->once())
->method('getFolder')
->with('images')
->willReturn($folder);
}
}
- public function testGetImageUrl() {
+ public function testGetImageUrl(): void {
$this->checkImagick();
- $file = $this->createMock(ISimpleFile::class);
$this->config->expects($this->exactly(2))
->method('getAppValue')
- ->withConsecutive(
- ['theming', 'cachebuster', '0'],
- ['theming', 'logoMime', '']
- )
- ->willReturn(0);
- $this->mockGetImage('logo', $file);
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '0'],
+ ['theming', 'logoMime', '', '0'],
+ ]);
$this->urlGenerator->expects($this->once())
->method('linkToRoute')
->willReturn('url-to-image');
$this->assertEquals('url-to-image?v=0', $this->imageManager->getImageUrl('logo', false));
}
- public function testGetImageUrlDefault() {
+ public function testGetImageUrlDefault(): void {
$this->config->expects($this->exactly(2))
->method('getAppValue')
- ->withConsecutive(
- ['theming', 'cachebuster', '0'],
- ['theming', 'logoMime', false]
- )
- ->willReturnOnConsecutiveCalls(0, false);
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '0'],
+ ['theming', 'logoMime', '', ''],
+ ]);
$this->urlGenerator->expects($this->once())
->method('imagePath')
->with('core', 'logo/logo.png')
@@ -159,30 +134,21 @@ class ImageManagerTest extends TestCase {
$this->assertEquals('logo/logo.png?v=0', $this->imageManager->getImageUrl('logo'));
}
- public function testGetImageUrlAbsolute() {
+ public function testGetImageUrlAbsolute(): void {
$this->checkImagick();
- $file = $this->createMock(ISimpleFile::class);
$this->config->expects($this->exactly(2))
->method('getAppValue')
- ->withConsecutive(
- ['theming', 'cachebuster', '0'],
- ['theming', 'logoMime', '']
- )
- ->willReturn(0);
- $this->mockGetImage('logo', $file);
- $this->urlGenerator->expects($this->at(0))
- ->method('getBaseUrl')
- ->willReturn('baseurl');
- $this->urlGenerator->expects($this->at(1))
- ->method('getAbsoluteUrl')
- ->willReturn('url-to-image-absolute?v=0');
- $this->urlGenerator->expects($this->at(2))
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '0'],
+ ['theming', 'logoMime', '', ''],
+ ]);
+ $this->urlGenerator->expects($this->any())
->method('getAbsoluteUrl')
->willReturn('url-to-image-absolute?v=0');
$this->assertEquals('url-to-image-absolute?v=0', $this->imageManager->getImageUrlAbsolute('logo', false));
}
- public function testGetImage() {
+ public function testGetImage(): void {
$this->checkImagick();
$this->config->expects($this->once())
->method('getAppValue')->with('theming', 'logoMime', false)
@@ -193,8 +159,8 @@ class ImageManagerTest extends TestCase {
}
- public function testGetImageUnset() {
- $this->expectException(\OCP\Files\NotFoundException::class);
+ public function testGetImageUnset(): void {
+ $this->expectException(NotFoundException::class);
$this->config->expects($this->once())
->method('getAppValue')->with('theming', 'logoMime', false)
@@ -202,42 +168,42 @@ class ImageManagerTest extends TestCase {
$this->imageManager->getImage('logo');
}
- public function testGetCacheFolder() {
+ public function testGetCacheFolder(): void {
$folder = $this->createMock(ISimpleFolder::class);
$this->config->expects($this->once())
->method('getAppValue')
->with('theming', 'cachebuster', '0')
->willReturn('0');
- $this->appData->expects($this->at(0))
+ $this->rootFolder->expects($this->once())
->method('getFolder')
->with('0')
->willReturn($folder);
$this->assertEquals($folder, $this->imageManager->getCacheFolder());
}
- public function testGetCacheFolderCreate() {
+ public function testGetCacheFolderCreate(): void {
$folder = $this->createMock(ISimpleFolder::class);
$this->config->expects($this->exactly(2))
->method('getAppValue')
->with('theming', 'cachebuster', '0')
->willReturn('0');
- $this->appData->expects($this->at(0))
+ $this->rootFolder->expects($this->exactly(2))
->method('getFolder')
- ->willThrowException(new NotFoundException());
- $this->appData->expects($this->at(1))
- ->method('newFolder')
->with('0')
- ->willReturn($folder);
- $this->appData->expects($this->at(2))
- ->method('getFolder')
+ ->willReturnOnConsecutiveCalls(
+ $this->throwException(new NotFoundException()),
+ $folder,
+ );
+ $this->rootFolder->expects($this->once())
+ ->method('newFolder')
->with('0')
->willReturn($folder);
- $this->appData->expects($this->once())
+ $this->rootFolder->expects($this->once())
->method('getDirectoryListing')
->willReturn([]);
$this->assertEquals($folder, $this->imageManager->getCacheFolder());
}
- public function testGetCachedImage() {
+ public function testGetCachedImage(): void {
$expected = $this->createMock(ISimpleFile::class);
$folder = $this->setupCacheFolder();
$folder->expects($this->once())
@@ -248,18 +214,18 @@ class ImageManagerTest extends TestCase {
}
- public function testGetCachedImageNotFound() {
- $this->expectException(\OCP\Files\NotFoundException::class);
+ public function testGetCachedImageNotFound(): void {
+ $this->expectException(NotFoundException::class);
$folder = $this->setupCacheFolder();
$folder->expects($this->once())
->method('getFile')
->with('filename')
- ->will($this->throwException(new \OCP\Files\NotFoundException()));
+ ->willThrowException(new NotFoundException());
$image = $this->imageManager->getCachedImage('filename');
}
- public function testSetCachedImage() {
+ public function testSetCachedImage(): void {
$folder = $this->setupCacheFolder();
$file = $this->createMock(ISimpleFile::class);
$folder->expects($this->once())
@@ -276,7 +242,7 @@ class ImageManagerTest extends TestCase {
$this->assertEquals($file, $this->imageManager->setCachedImage('filename', 'filecontent'));
}
- public function testSetCachedImageCreate() {
+ public function testSetCachedImageCreate(): void {
$folder = $this->setupCacheFolder();
$file = $this->createMock(ISimpleFile::class);
$folder->expects($this->once())
@@ -299,14 +265,14 @@ class ImageManagerTest extends TestCase {
->method('getAppValue')
->with('theming', 'cachebuster', '0')
->willReturn('0');
- $this->appData->expects($this->at(0))
+ $this->rootFolder->expects($this->once())
->method('getFolder')
->with('0')
->willReturn($folder);
return $folder;
}
- public function testCleanup() {
+ public function testCleanup(): void {
$folders = [
$this->createMock(ISimpleFolder::class),
$this->createMock(ISimpleFolder::class),
@@ -315,19 +281,19 @@ class ImageManagerTest extends TestCase {
foreach ($folders as $index => $folder) {
$folder->expects($this->any())
->method('getName')
- ->willReturn($index);
+ ->willReturn("$index");
}
$folders[0]->expects($this->once())->method('delete');
$folders[1]->expects($this->once())->method('delete');
$folders[2]->expects($this->never())->method('delete');
$this->config->expects($this->once())
->method('getAppValue')
- ->with('theming','cachebuster','0')
+ ->with('theming', 'cachebuster', '0')
->willReturn('2');
- $this->appData->expects($this->once())
+ $this->rootFolder->expects($this->once())
->method('getDirectoryListing')
->willReturn($folders);
- $this->appData->expects($this->once())
+ $this->rootFolder->expects($this->once())
->method('getFolder')
->with('2')
->willReturn($folders[2]);
@@ -335,43 +301,46 @@ class ImageManagerTest extends TestCase {
}
- public function dataUpdateImage() {
+ public static function dataUpdateImage(): array {
return [
- ['background', __DIR__ . '/../../../tests/data/testimage.png', true, true],
- ['background', __DIR__ . '/../../../tests/data/testimage.png', false, true],
- ['background', __DIR__ . '/../../../tests/data/testimage.jpg', true, true],
+ ['background', __DIR__ . '/../../../tests/data/testimage.png', true, false],
+ ['background', __DIR__ . '/../../../tests/data/testimage.png', false, false],
+ ['background', __DIR__ . '/../../../tests/data/testimage.jpg', true, false],
+ ['background', __DIR__ . '/../../../tests/data/testimage.webp', true, false],
+ ['background', __DIR__ . '/../../../tests/data/testimage-large.jpg', true, true],
+ ['background', __DIR__ . '/../../../tests/data/testimage-wide.png', true, true],
['logo', __DIR__ . '/../../../tests/data/testimagelarge.svg', true, false],
];
}
- /**
- * @dataProvider dataUpdateImage
- */
- public function testUpdateImage($key, $tmpFile, $folderExists, $shouldConvert) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateImage')]
+ public function testUpdateImage(string $key, string $tmpFile, bool $folderExists, bool $shouldConvert): void {
$file = $this->createMock(ISimpleFile::class);
$folder = $this->createMock(ISimpleFolder::class);
$oldFile = $this->createMock(ISimpleFile::class);
$folder->expects($this->any())
->method('getFile')
->willReturn($oldFile);
+
if ($folderExists) {
- $this->appData
+ $this->rootFolder
->expects($this->any())
->method('getFolder')
->with('images')
->willReturn($folder);
} else {
- $this->appData
+ $this->rootFolder
->expects($this->any())
->method('getFolder')
->with('images')
->willThrowException(new NotFoundException());
- $this->appData
+ $this->rootFolder
->expects($this->any())
->method('newFolder')
->with('images')
->willReturn($folder);
}
+
$folder->expects($this->once())
->method('newFile')
->with($key)
@@ -385,4 +354,30 @@ class ImageManagerTest extends TestCase {
$this->imageManager->updateImage($key, $tmpFile);
}
+
+ public function testUnsupportedImageType(): void {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('Unsupported image type: text/plain');
+
+ $file = $this->createMock(ISimpleFile::class);
+ $folder = $this->createMock(ISimpleFolder::class);
+ $oldFile = $this->createMock(ISimpleFile::class);
+
+ $folder->expects($this->any())
+ ->method('getFile')
+ ->willReturn($oldFile);
+
+ $this->rootFolder
+ ->expects($this->any())
+ ->method('getFolder')
+ ->with('images')
+ ->willReturn($folder);
+
+ $folder->expects($this->once())
+ ->method('newFile')
+ ->with('favicon')
+ ->willReturn($file);
+
+ $this->imageManager->updateImage('favicon', __DIR__ . '/../../../tests/data/lorem.txt');
+ }
}
diff --git a/apps/theming/tests/Migration/Version2006Date20240905111627Test.php b/apps/theming/tests/Migration/Version2006Date20240905111627Test.php
new file mode 100644
index 00000000000..5f7458db11a
--- /dev/null
+++ b/apps/theming/tests/Migration/Version2006Date20240905111627Test.php
@@ -0,0 +1,182 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Theming\Tests\Migration;
+
+use OCA\Theming\Migration\Version2006Date20240905111627;
+use OCP\BackgroundJob\IJobList;
+use OCP\Config\IUserConfig;
+use OCP\IAppConfig;
+use OCP\IDBConnection;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Server;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class Version2006Date20240905111627Test extends TestCase {
+
+ private IAppConfig&MockObject $appConfig;
+ private IDBConnection&MockObject $connection;
+ private IJobList&MockObject $jobList;
+ private Version2006Date20240905111627 $migration;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->appConfig = $this->createMock(IAppConfig::class);
+ $this->connection = $this->createMock(IDBConnection::class);
+ $this->jobList = $this->createMock(IJobList::class);
+ $this->migration = new Version2006Date20240905111627(
+ $this->jobList,
+ $this->appConfig,
+ $this->connection,
+ );
+ }
+
+ public function testRestoreSystemColors(): void {
+ $this->appConfig->expects(self::once())
+ ->method('getValueString')
+ ->with('theming', 'color', '')
+ ->willReturn('ffab00');
+ $this->appConfig->expects(self::once())
+ ->method('getValueBool')
+ ->with('theming', 'disable-user-theming')
+ ->willReturn(true);
+
+ // expect the color value to be deleted
+ $this->appConfig->expects(self::once())
+ ->method('deleteKey')
+ ->with('theming', 'color');
+ // expect the correct calls to setValueString (setting the new values)
+ $setValueCalls = [];
+ $this->appConfig->expects(self::exactly(2))
+ ->method('setValueString')
+ ->willReturnCallback(function () use (&$setValueCalls) {
+ $setValueCalls[] = func_get_args();
+ return true;
+ });
+
+ $output = $this->createMock(IOutput::class);
+ $this->migration->changeSchema($output, fn () => null, []);
+
+ $this->assertEquals([
+ ['theming', 'background_color', 'ffab00', false, false],
+ ['theming', 'primary_color', 'ffab00', false, false],
+ ], $setValueCalls);
+ }
+
+ /**
+ * @group DB
+ */
+ public function testRestoreUserColors(): void {
+ $this->appConfig->expects(self::once())
+ ->method('getValueString')
+ ->with('theming', 'color', '')
+ ->willReturn('');
+ $this->appConfig->expects(self::once())
+ ->method('getValueBool')
+ ->with('theming', 'disable-user-theming')
+ ->willReturn(false);
+
+ // Create a user
+ $manager = Server::get(IUserManager::class);
+ $user = $manager->createUser('theming_legacy', 'theming_legacy');
+ self::assertNotFalse($user);
+ // Set the users theming value to legacy key
+ $config = Server::get(IUserConfig::class);
+ $config->setValueString('theming_legacy', 'theming', 'background_color', 'ffab00');
+
+ // expect some output
+ $output = $this->createMock(IOutput::class);
+ $output->expects(self::exactly(3))
+ ->method('info')
+ ->willReturnCallback(fn ($txt) => match($txt) {
+ 'No custom system color configured - skipping' => true,
+ 'Restoring user primary color' => true,
+ 'Primary color of users restored' => true,
+ default => self::fail('output.info called with unexpected argument: ' . $txt)
+ });
+ // Create the migration class
+ $migration = new Version2006Date20240905111627(
+ $this->jobList,
+ $this->appConfig,
+ Server::get(IDBConnection::class),
+ );
+ // Run the migration
+ $migration->changeSchema($output, fn () => null, []);
+
+ // See new value
+ $config->clearCache('theming_legacy');
+ $newValue = $config->getValueString('theming_legacy', 'theming', 'primary_color');
+ self::assertEquals('ffab00', $newValue);
+
+ // cleanup
+ $user->delete();
+ }
+
+ /**
+ * Ensure only users with background color but no primary color are migrated
+ * @group DB
+ */
+ public function testRestoreUserColorsWithConflicts(): void {
+ $this->appConfig->expects(self::once())
+ ->method('getValueString')
+ ->with('theming', 'color', '')
+ ->willReturn('');
+ $this->appConfig->expects(self::once())
+ ->method('getValueBool')
+ ->with('theming', 'disable-user-theming')
+ ->willReturn(false);
+
+ // Create a user
+ $manager = Server::get(IUserManager::class);
+ $legacyUser = $manager->createUser('theming_legacy', 'theming_legacy');
+ self::assertNotFalse($legacyUser);
+ $user = $manager->createUser('theming_no_legacy', 'theming_no_legacy');
+ self::assertNotFalse($user);
+ // Set the users theming value to legacy key
+ $config = Server::get(IUserConfig::class);
+ $config->setValueString($user->getUID(), 'theming', 'primary_color', '999999');
+ $config->setValueString($user->getUID(), 'theming', 'background_color', '111111');
+ $config->setValueString($legacyUser->getUID(), 'theming', 'background_color', 'ffab00');
+
+ // expect some output
+ $output = $this->createMock(IOutput::class);
+ $output->expects(self::exactly(3))
+ ->method('info')
+ ->willReturnCallback(fn ($txt) => match($txt) {
+ 'No custom system color configured - skipping' => true,
+ 'Restoring user primary color' => true,
+ 'Primary color of users restored' => true,
+ default => self::fail('output.info called with unexpected argument: ' . $txt)
+ });
+ // Create the migration class
+ $migration = new Version2006Date20240905111627(
+ $this->jobList,
+ $this->appConfig,
+ Server::get(IDBConnection::class),
+ );
+ // Run the migration
+ $migration->changeSchema($output, fn () => null, []);
+
+ // See new value of only the legacy user
+ $config->clearCacheAll();
+ self::assertEquals('111111', $config->getValueString($user->getUID(), 'theming', 'background_color'));
+ self::assertEquals('999999', $config->getValueString($user->getUID(), 'theming', 'primary_color'));
+ self::assertEquals('ffab00', $config->getValueString($legacyUser->getUID(), 'theming', 'primary_color'));
+
+ // cleanup
+ $legacyUser->delete();
+ $user->delete();
+ }
+}
diff --git a/apps/theming/tests/Service/ThemesServiceTest.php b/apps/theming/tests/Service/ThemesServiceTest.php
new file mode 100644
index 00000000000..354ed1dec85
--- /dev/null
+++ b/apps/theming/tests/Service/ThemesServiceTest.php
@@ -0,0 +1,369 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Service;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\ThemesService;
+use OCA\Theming\Themes\DarkHighContrastTheme;
+use OCA\Theming\Themes\DarkTheme;
+use OCA\Theming\Themes\DefaultTheme;
+use OCA\Theming\Themes\DyslexiaFont;
+use OCA\Theming\Themes\HighContrastTheme;
+use OCA\Theming\Themes\LightTheme;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class ThemesServiceTest extends TestCase {
+ private IUserSession&MockObject $userSession;
+ private IConfig&MockObject $config;
+ private LoggerInterface&MockObject $logger;
+
+ private ThemingDefaults&MockObject $themingDefaults;
+ private ThemesService $themesService;
+
+ /** @var ITheme[] */
+ private array $themes;
+
+ protected function setUp(): void {
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+
+ $this->themingDefaults->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->initThemes();
+
+ $this->themesService = new ThemesService(
+ $this->userSession,
+ $this->config,
+ $this->logger,
+ ...array_values($this->themes)
+ );
+
+ parent::setUp();
+ }
+
+ public function testGetThemes(): void {
+ $expected = [
+ 'default',
+ 'light',
+ 'dark',
+ 'light-highcontrast',
+ 'dark-highcontrast',
+ 'opendyslexic',
+ ];
+ $this->assertEquals($expected, array_keys($this->themesService->getThemes()));
+ }
+
+ public function testGetThemesEnforced(): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('enforce_theme', '')
+ ->willReturn('dark');
+ $this->logger->expects($this->never())
+ ->method('error');
+
+ $expected = [
+ 'default',
+ 'dark',
+ ];
+
+ $this->assertEquals($expected, array_keys($this->themesService->getThemes()));
+ }
+
+ public function testGetThemesEnforcedInvalid(): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('enforce_theme', '')
+ ->willReturn('invalid');
+ $this->logger->expects($this->once())
+ ->method('error')
+ ->with('Enforced theme not found', ['theme' => 'invalid']);
+
+ $expected = [
+ 'default',
+ 'light',
+ 'dark',
+ 'light-highcontrast',
+ 'dark-highcontrast',
+ 'opendyslexic',
+ ];
+
+ $this->assertEquals($expected, array_keys($this->themesService->getThemes()));
+ }
+
+ public static function dataTestEnableTheme(): array {
+ return [
+ ['default', ['default'], ['default']],
+ ['dark', ['default'], ['dark']],
+ ['dark', ['dark'], ['dark']],
+ ['opendyslexic', ['dark'], ['dark', 'opendyslexic']],
+ ['dark', ['light-highcontrast', 'opendyslexic'], ['opendyslexic', 'dark']],
+ ];
+ }
+
+ /**
+ *
+ * @param string[] $enabledThemes
+ * @param string[] $expectedEnabled
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestEnableTheme')]
+ public function testEnableTheme(string $toEnable, array $enabledThemes, array $expectedEnabled): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('user', Application::APP_ID, 'enabled-themes', '["default"]')
+ ->willReturn(json_encode($enabledThemes));
+
+ $this->assertEquals($expectedEnabled, $this->themesService->enableTheme($this->themes[$toEnable]));
+ }
+
+
+ public static function dataTestDisableTheme(): array {
+ return [
+ ['dark', ['default'], ['default']],
+ ['dark', ['dark'], []],
+ ['opendyslexic', ['dark', 'opendyslexic'], ['dark'], ],
+ ['light-highcontrast', ['opendyslexic'], ['opendyslexic']],
+ ];
+ }
+
+ /**
+ *
+ * @param string[] $enabledThemes
+ * @param string[] $expectedEnabled
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestDisableTheme')]
+ public function testDisableTheme(string $toDisable, array $enabledThemes, array $expectedEnabled): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('user', Application::APP_ID, 'enabled-themes', '["default"]')
+ ->willReturn(json_encode($enabledThemes));
+
+
+ $this->assertEquals($expectedEnabled, $this->themesService->disableTheme($this->themes[$toDisable]));
+ }
+
+
+ public static function dataTestIsEnabled(): array {
+ return [
+ ['dark', [], false],
+ ['dark', ['dark'], true],
+ ['opendyslexic', ['dark', 'opendyslexic'], true],
+ ['light-highcontrast', ['opendyslexic'], false],
+ ];
+ }
+
+ /**
+ * @param string[] $enabledThemes
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsEnabled')]
+ public function testIsEnabled(string $themeId, array $enabledThemes, bool $expected): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('user', Application::APP_ID, 'enabled-themes', '["default"]')
+ ->willReturn(json_encode($enabledThemes));
+
+
+ $this->assertEquals($expected, $this->themesService->isEnabled($this->themes[$themeId]));
+ }
+
+ public function testGetEnabledThemes(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('user', Application::APP_ID, 'enabled-themes', '["default"]')
+ ->willReturn(json_encode(['default']));
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('enforce_theme', '')
+ ->willReturn('');
+
+ $this->assertEquals(['default'], $this->themesService->getEnabledThemes());
+ }
+
+ public function testGetEnabledThemesEnforced(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('user', Application::APP_ID, 'enabled-themes', '["default"]')
+ ->willReturn(json_encode([]));
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('enforce_theme', '')
+ ->willReturn('light');
+
+ $this->assertEquals(['light'], $this->themesService->getEnabledThemes());
+ }
+
+
+ public static function dataTestSetEnabledThemes(): array {
+ return [
+ [[], []],
+ [['light'], ['light']],
+ [['dark'], ['dark']],
+ [['dark', 'dark', 'opendyslexic'], ['dark', 'opendyslexic']],
+ ];
+ }
+
+ /**
+ *
+ * @param string[] $enabledThemes
+ * @param string[] $expected
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSetEnabledThemes')]
+ public function testSetEnabledThemes(array $enabledThemes, array $expected): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+
+ $this->config->expects($this->once())
+ ->method('setUserValue')
+ ->with('user', Application::APP_ID, 'enabled-themes', json_encode($expected));
+
+ $this->invokePrivate($this->themesService, 'setEnabledThemes', [$enabledThemes]);
+ }
+
+ private function initThemes() {
+ $util = $this->createMock(Util::class);
+ $urlGenerator = $this->createMock(IURLGenerator::class);
+ $imageManager = $this->createMock(ImageManager::class);
+ $l10n = $this->createMock(IL10N::class);
+ $appManager = $this->createMock(IAppManager::class);
+
+ $this->themes = [
+ 'default' => new DefaultTheme(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $urlGenerator,
+ $imageManager,
+ $this->config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'light' => new LightTheme(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $urlGenerator,
+ $imageManager,
+ $this->config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'dark' => new DarkTheme(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $urlGenerator,
+ $imageManager,
+ $this->config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'light-highcontrast' => new HighContrastTheme(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $urlGenerator,
+ $imageManager,
+ $this->config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'dark-highcontrast' => new DarkHighContrastTheme(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $urlGenerator,
+ $imageManager,
+ $this->config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'opendyslexic' => new DyslexiaFont(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $urlGenerator,
+ $imageManager,
+ $this->config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ ];
+ }
+}
diff --git a/apps/theming/tests/ServicesTest.php b/apps/theming/tests/ServicesTest.php
index 70d8f402afa..3971c9b6698 100644
--- a/apps/theming/tests/ServicesTest.php
+++ b/apps/theming/tests/ServicesTest.php
@@ -1,38 +1,20 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests;
use OCA\Theming\Capabilities;
use OCA\Theming\Controller\ThemingController;
use OCA\Theming\Settings\Admin;
-use OCA\Theming\Settings\Section;
+use OCA\Theming\Settings\PersonalSection;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
use OCP\AppFramework\App;
+use OCP\AppFramework\IAppContainer;
use OCP\Capabilities\ICapability;
use OCP\IL10N;
use OCP\Settings\IIconSection;
@@ -46,11 +28,9 @@ use Test\TestCase;
* @package OCA\Theming\Tests
*/
class ServicesTest extends TestCase {
- /** @var \OCA\Activity\AppInfo\Application */
- protected $app;
+ protected App $app;
- /** @var \OCP\AppFramework\IAppContainer */
- protected $container;
+ protected IAppContainer $container;
protected function setUp(): void {
parent::setUp();
@@ -58,7 +38,7 @@ class ServicesTest extends TestCase {
$this->container = $this->app->getContainer();
}
- public function queryData() {
+ public static function queryData(): array {
return [
[IL10N::class],
@@ -75,20 +55,16 @@ class ServicesTest extends TestCase {
// Settings
[Admin::class],
[Admin::class, ISettings::class],
- [Section::class],
- [Section::class, IIconSection::class],
+ [PersonalSection::class],
+ [PersonalSection::class, IIconSection::class],
];
}
- /**
- * @dataProvider queryData
- * @param string $service
- * @param string $expected
- */
- public function testContainerQuery($service, $expected = null) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('queryData')]
+ public function testContainerQuery(string $service, ?string $expected = null): void {
if ($expected === null) {
$expected = $service;
}
- $this->assertTrue($this->container->query($service) instanceof $expected);
+ $this->assertInstanceOf($expected, $this->container->query($service));
}
}
diff --git a/apps/theming/tests/Settings/AdminSectionTest.php b/apps/theming/tests/Settings/AdminSectionTest.php
new file mode 100644
index 00000000000..ecb889f264b
--- /dev/null
+++ b/apps/theming/tests/Settings/AdminSectionTest.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Settings;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\Settings\AdminSection;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class AdminSectionTest extends TestCase {
+ private IURLGenerator&MockObject $url;
+ private IL10N&MockObject $l;
+ private AdminSection $section;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->url = $this->createMock(IURLGenerator::class);
+ $this->l = $this->createMock(IL10N::class);
+
+ $this->section = new AdminSection(
+ Application::APP_ID,
+ $this->url,
+ $this->l
+ );
+ }
+
+ public function testGetID(): void {
+ $this->assertSame('theming', $this->section->getID());
+ }
+
+ public function testGetName(): void {
+ $this->l
+ ->expects($this->once())
+ ->method('t')
+ ->with('Theming')
+ ->willReturn('Theming');
+
+ $this->assertSame('Theming', $this->section->getName());
+ }
+
+ public function testGetPriority(): void {
+ $this->assertSame(30, $this->section->getPriority());
+ }
+
+ public function testGetIcon(): void {
+ $this->url->expects($this->once())
+ ->method('imagePath')
+ ->with('theming', 'app-dark.svg')
+ ->willReturn('icon');
+
+ $this->assertSame('icon', $this->section->getIcon());
+ }
+}
diff --git a/apps/theming/tests/Settings/AdminTest.php b/apps/theming/tests/Settings/AdminTest.php
index e5de7093f8f..277b94900a8 100644
--- a/apps/theming/tests/Settings/AdminTest.php
+++ b/apps/theming/tests/Settings/AdminTest.php
@@ -1,74 +1,58 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests\Settings;
+use OCA\Theming\AppInfo\Application;
use OCA\Theming\ImageManager;
use OCA\Theming\Settings\Admin;
use OCA\Theming\ThemingDefaults;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\IConfig;
use OCP\IL10N;
+use OCP\INavigationManager;
use OCP\IURLGenerator;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class AdminTest extends TestCase {
- /** @var Admin */
- private $admin;
- /** @var IConfig */
- private $config;
- /** @var ThemingDefaults */
- private $themingDefaults;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var ImageManager */
- private $imageManager;
- /** @var IL10N */
- private $l10n;
+ private Admin $admin;
+ private IConfig&MockObject $config;
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IInitialState&MockObject $initialState;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ImageManager&MockObject $imageManager;
+ private IL10N&MockObject $l10n;
+ private INavigationManager&MockObject $navigationManager;
protected function setUp(): void {
parent::setUp();
$this->config = $this->createMock(IConfig::class);
$this->l10n = $this->createMock(IL10N::class);
$this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->initialState = $this->createMock(IInitialState::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->imageManager = $this->createMock(ImageManager::class);
+ $this->navigationManager = $this->createMock(INavigationManager::class);
$this->admin = new Admin(
+ Application::APP_ID,
$this->config,
$this->l10n,
$this->themingDefaults,
+ $this->initialState,
$this->urlGenerator,
- $this->imageManager
+ $this->imageManager,
+ $this->navigationManager,
);
}
- public function testGetFormNoErrors() {
+ public function testGetFormNoErrors(): void {
$this->config
->expects($this->once())
->method('getSystemValue')
@@ -96,33 +80,14 @@ class AdminTest extends TestCase {
->willReturn('MySlogan');
$this->themingDefaults
->expects($this->once())
- ->method('getColorPrimary')
+ ->method('getDefaultColorPrimary')
->willReturn('#fff');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRoute')
- ->with('theming.Theming.uploadImage')
- ->willReturn('/my/route');
- $params = [
- 'themable' => true,
- 'errorMessage' => '',
- 'name' => 'MyEntity',
- 'url' => 'https://example.com',
- 'slogan' => 'MySlogan',
- 'color' => '#fff',
- 'uploadLogoRoute' => '/my/route',
- 'canThemeIcons' => null,
- 'iconDocs' => null,
- 'images' => [],
- 'imprintUrl' => '',
- 'privacyUrl' => '',
- ];
- $expected = new TemplateResponse('theming', 'settings-admin', $params, '');
+ $expected = new TemplateResponse('theming', 'settings-admin');
$this->assertEquals($expected, $this->admin->getForm());
}
- public function testGetFormWithErrors() {
+ public function testGetFormWithErrors(): void {
$this->config
->expects($this->once())
->method('getSystemValue')
@@ -155,37 +120,18 @@ class AdminTest extends TestCase {
->willReturn('MySlogan');
$this->themingDefaults
->expects($this->once())
- ->method('getColorPrimary')
+ ->method('getDefaultColorPrimary')
->willReturn('#fff');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRoute')
- ->with('theming.Theming.uploadImage')
- ->willReturn('/my/route');
- $params = [
- 'themable' => false,
- 'errorMessage' => 'You are already using a custom theme. Theming app settings might be overwritten by that.',
- 'name' => 'MyEntity',
- 'url' => 'https://example.com',
- 'slogan' => 'MySlogan',
- 'color' => '#fff',
- 'uploadLogoRoute' => '/my/route',
- 'canThemeIcons' => null,
- 'iconDocs' => '',
- 'images' => [],
- 'imprintUrl' => '',
- 'privacyUrl' => '',
- ];
- $expected = new TemplateResponse('theming', 'settings-admin', $params, '');
+ $expected = new TemplateResponse('theming', 'settings-admin');
$this->assertEquals($expected, $this->admin->getForm());
}
- public function testGetSection() {
+ public function testGetSection(): void {
$this->assertSame('theming', $this->admin->getSection());
}
- public function testGetPriority() {
+ public function testGetPriority(): void {
$this->assertSame(5, $this->admin->getPriority());
}
}
diff --git a/apps/theming/tests/Settings/PersonalTest.php b/apps/theming/tests/Settings/PersonalTest.php
new file mode 100644
index 00000000000..9216450ec9c
--- /dev/null
+++ b/apps/theming/tests/Settings/PersonalTest.php
@@ -0,0 +1,236 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Settings;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\BackgroundService;
+use OCA\Theming\Service\ThemesService;
+use OCA\Theming\Settings\Personal;
+use OCA\Theming\Themes\DarkHighContrastTheme;
+use OCA\Theming\Themes\DarkTheme;
+use OCA\Theming\Themes\DefaultTheme;
+use OCA\Theming\Themes\DyslexiaFont;
+use OCA\Theming\Themes\HighContrastTheme;
+use OCA\Theming\Themes\LightTheme;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\INavigationManager;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class PersonalTest extends TestCase {
+ private IConfig&MockObject $config;
+ private ThemesService&MockObject $themesService;
+ private IInitialState&MockObject $initialStateService;
+ private ThemingDefaults&MockObject $themingDefaults;
+ private INavigationManager&MockObject $navigationManager;
+ private Personal $admin;
+
+ /** @var ITheme[] */
+ private array $themes;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->config = $this->createMock(IConfig::class);
+ $this->themesService = $this->createMock(ThemesService::class);
+ $this->initialStateService = $this->createMock(IInitialState::class);
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->navigationManager = $this->createMock(INavigationManager::class);
+
+ $this->initThemes();
+
+ $this->themesService
+ ->expects($this->any())
+ ->method('getThemes')
+ ->willReturn($this->themes);
+
+ $this->admin = new Personal(
+ Application::APP_ID,
+ 'admin',
+ $this->config,
+ $this->themesService,
+ $this->initialStateService,
+ $this->themingDefaults,
+ $this->navigationManager,
+ );
+ }
+
+ public function dataTestGetForm(): array {
+ return [
+ ['', [
+ $this->formatThemeForm('default'),
+ $this->formatThemeForm('light'),
+ $this->formatThemeForm('dark'),
+ $this->formatThemeForm('light-highcontrast'),
+ $this->formatThemeForm('dark-highcontrast'),
+ $this->formatThemeForm('opendyslexic'),
+ ]],
+ ['dark', [
+ $this->formatThemeForm('dark'),
+ $this->formatThemeForm('opendyslexic'),
+ ]],
+ ];
+ }
+
+ /**
+ * @param string[] $enabledThemes
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetForm')]
+ public function testGetForm(string $enforcedTheme, array $themesState): void {
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('enforce_theme', '')
+ ->willReturn($enforcedTheme);
+
+ $this->config->expects($this->any())
+ ->method('getUserValue')
+ ->willReturnMap([
+ ['admin', 'core', 'apporder', '[]', '[]'],
+ ['admin', 'theming', 'background_image', BackgroundService::BACKGROUND_DEFAULT],
+ ]);
+
+ $this->navigationManager->expects($this->once())
+ ->method('getDefaultEntryIdForUser')
+ ->willReturn('forced_id');
+
+ $this->initialStateService->expects($this->exactly(8))
+ ->method('provideInitialState')
+ ->willReturnMap([
+ ['shippedBackgrounds', BackgroundService::SHIPPED_BACKGROUNDS],
+ ['themingDefaults'],
+ ['enableBlurFilter', ''],
+ ['userBackgroundImage'],
+ ['themes', $themesState],
+ ['enforceTheme', $enforcedTheme],
+ ['isUserThemingDisabled', false],
+ ['navigationBar', ['userAppOrder' => [], 'enforcedDefaultApp' => 'forced_id']],
+ ]);
+
+ $expected = new TemplateResponse('theming', 'settings-personal');
+ $this->assertEquals($expected, $this->admin->getForm());
+ }
+
+ public function testGetSection(): void {
+ $this->assertSame('theming', $this->admin->getSection());
+ }
+
+ public function testGetPriority(): void {
+ $this->assertSame(40, $this->admin->getPriority());
+ }
+
+ private function initThemes() {
+ $util = $this->createMock(Util::class);
+ $themingDefaults = $this->createMock(ThemingDefaults::class);
+ $userSession = $this->createMock(IUserSession::class);
+ $urlGenerator = $this->createMock(IURLGenerator::class);
+ $imageManager = $this->createMock(ImageManager::class);
+ $config = $this->createMock(IConfig::class);
+ $l10n = $this->createMock(IL10N::class);
+ $appManager = $this->createMock(IAppManager::class);
+
+ $themingDefaults->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn('#0082c9');
+
+ $themingDefaults->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themes = [
+ 'default' => new DefaultTheme(
+ $util,
+ $themingDefaults,
+ $userSession,
+ $urlGenerator,
+ $imageManager,
+ $config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'light' => new LightTheme(
+ $util,
+ $themingDefaults,
+ $userSession,
+ $urlGenerator,
+ $imageManager,
+ $config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'dark' => new DarkTheme(
+ $util,
+ $themingDefaults,
+ $userSession,
+ $urlGenerator,
+ $imageManager,
+ $config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'light-highcontrast' => new HighContrastTheme(
+ $util,
+ $themingDefaults,
+ $userSession,
+ $urlGenerator,
+ $imageManager,
+ $config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'dark-highcontrast' => new DarkHighContrastTheme(
+ $util,
+ $themingDefaults,
+ $userSession,
+ $urlGenerator,
+ $imageManager,
+ $config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ 'opendyslexic' => new DyslexiaFont(
+ $util,
+ $themingDefaults,
+ $userSession,
+ $urlGenerator,
+ $imageManager,
+ $config,
+ $l10n,
+ $appManager,
+ null,
+ ),
+ ];
+ }
+
+ private function formatThemeForm(string $themeId): array {
+ $this->initThemes();
+
+ $theme = $this->themes[$themeId];
+ return [
+ 'id' => $theme->getId(),
+ 'type' => $theme->getType(),
+ 'title' => $theme->getTitle(),
+ 'enableLabel' => $theme->getEnableLabel(),
+ 'description' => $theme->getDescription(),
+ 'enabled' => false,
+ ];
+ }
+}
diff --git a/apps/theming/tests/Settings/SectionTest.php b/apps/theming/tests/Settings/SectionTest.php
deleted file mode 100644
index 81fb3f07409..00000000000
--- a/apps/theming/tests/Settings/SectionTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
- *
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-namespace OCA\Theming\Tests\Settings;
-
-use OCA\Theming\Settings\Section;
-use OCP\IL10N;
-use OCP\IURLGenerator;
-use Test\TestCase;
-
-class SectionTest extends TestCase {
- /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
- private $url;
- /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
- private $l;
- /** @var Section */
- private $section;
-
- protected function setUp(): void {
- parent::setUp();
- $this->url = $this->createMock(IURLGenerator::class);
- $this->l = $this->createMock(IL10N::class);
-
- $this->section = new Section(
- $this->url,
- $this->l
- );
- }
-
- public function testGetID() {
- $this->assertSame('theming', $this->section->getID());
- }
-
- public function testGetName() {
- $this->l
- ->expects($this->once())
- ->method('t')
- ->with('Theming')
- ->willReturn('Theming');
-
- $this->assertSame('Theming', $this->section->getName());
- }
-
- public function testGetPriority() {
- $this->assertSame(30, $this->section->getPriority());
- }
-
- public function testGetIcon() {
- $this->url->expects($this->once())
- ->method('imagePath')
- ->with('theming', 'app-dark.svg')
- ->willReturn('icon');
-
- $this->assertSame('icon', $this->section->getIcon());
- }
-}
diff --git a/apps/theming/tests/Themes/AccessibleThemeTestCase.php b/apps/theming/tests/Themes/AccessibleThemeTestCase.php
new file mode 100644
index 00000000000..f516e1f5116
--- /dev/null
+++ b/apps/theming/tests/Themes/AccessibleThemeTestCase.php
@@ -0,0 +1,172 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Themes;
+
+use OCA\Theming\ITheme;
+use OCA\Theming\Util;
+use Test\TestCase;
+
+class AccessibleThemeTestCase extends TestCase {
+ protected ITheme $theme;
+ protected Util $util;
+
+ /**
+ * Set to true to check for WCAG AAA level accessibility
+ */
+ protected static bool $WCAGaaa = false;
+
+ public static function dataAccessibilityPairs(): array {
+ $textContrast = self::$WCAGaaa ? 7.0 : 4.5;
+ $elementContrast = 3.0;
+
+ return [
+ 'primary-element on background' => [
+ [
+ '--color-primary-element',
+ '--color-primary-element-hover',
+ ],
+ [
+ '--color-main-background',
+ '--color-background-hover',
+ '--color-background-dark',
+ '--color-background-darker',
+ '--color-main-background-blur',
+ ],
+ $elementContrast,
+ ],
+ 'status color elements on background' => [
+ [
+ '--color-error',
+ '--color-error-hover',
+ '--color-warning',
+ '--color-warning-hover',
+ '--color-info',
+ '--color-info-hover',
+ '--color-success',
+ '--color-success-hover',
+ '--color-favorite',
+ ],
+ [
+ '--color-main-background',
+ '--color-background-hover',
+ '--color-background-dark',
+ '--color-background-darker',
+ '--color-main-background-blur',
+ ],
+ $elementContrast,
+ ],
+ 'border-colors' => [
+ [
+ '--color-border-maxcontrast',
+ ],
+ [
+ '--color-main-background',
+ '--color-background-hover',
+ '--color-background-dark',
+ '--color-main-background-blur',
+ ],
+ $elementContrast,
+ ],
+ // Those two colors are used for borders which will be `color-main-text` on focussed state, thus need 3:1 contrast to it
+ 'success-error-border-colors' => [
+ [
+ '--color-error',
+ '--color-success',
+ ],
+ [
+ '--color-main-text',
+ ],
+ $elementContrast,
+ ],
+ 'primary-element-text' => [
+ [
+ '--color-primary-element-text',
+ '--color-primary-element-text-dark',
+ ],
+ [
+ '--color-primary-element',
+ '--color-primary-element-hover',
+ ],
+ $textContrast,
+ ],
+ 'primary-element-light-text' => [
+ ['--color-primary-element-light-text'],
+ [
+ '--color-primary-element-light',
+ '--color-primary-element-light-hover',
+ ],
+ $textContrast,
+ ],
+ 'main-text' => [
+ ['--color-main-text'],
+ [
+ '--color-main-background',
+ '--color-background-hover',
+ '--color-background-dark',
+ '--color-background-darker',
+ '--color-main-background-blur',
+ ],
+ $textContrast,
+ ],
+ 'max-contrast-text' => [
+ ['--color-text-maxcontrast'],
+ [
+ '--color-main-background',
+ '--color-background-hover',
+ '--color-background-dark',
+ ],
+ $textContrast,
+ ],
+ 'max-contrast text-on blur' => [
+ ['--color-text-maxcontrast-background-blur'],
+ [
+ '--color-main-background-blur',
+ ],
+ $textContrast,
+ ],
+ 'status-text' => [
+ [
+ '--color-error-text',
+ '--color-warning-text',
+ '--color-success-text',
+ '--color-info-text',
+ ],
+ [
+ '--color-main-background',
+ '--color-background-hover',
+ '--color-background-dark',
+ '--color-main-background-blur',
+ ],
+ $textContrast,
+ ],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataAccessibilityPairs')]
+ public function testAccessibilityOfVariables(array $mainColors, array $backgroundColors, float $minContrast): void {
+ if (!isset($this->theme)) {
+ $this->markTestSkipped('You need to setup $this->theme in your setUp function');
+ } elseif (!isset($this->util)) {
+ $this->markTestSkipped('You need to setup $this->util in your setUp function');
+ }
+
+ $variables = $this->theme->getCSSVariables();
+
+ // Blur effect does not work so we mockup the color - worst supported case is the default "clouds" background image (on dark themes the clouds with white color are bad on bright themes the primary color as sky is bad)
+ $variables['--color-main-background-blur'] = $this->util->mix($variables['--color-main-background'], $this->util->isBrightColor($variables['--color-main-background']) ? '#000000' : '#ffffff', 75);
+
+ foreach ($backgroundColors as $background) {
+ $this->assertStringStartsWith('#', $variables[$background], 'Is not a plain color variable - consider to remove or fix this test');
+ foreach ($mainColors as $main) {
+ $this->assertStringStartsWith('#', $variables[$main], 'Is not a plain color variable - consider to remove or fix this test');
+ $realContrast = $this->util->colorContrast($variables[$main], $variables[$background]);
+ $this->assertGreaterThanOrEqual($minContrast, $realContrast, "Contrast is not high enough for $main (" . $variables[$main] . ") on $background (" . $variables[$background] . ')');
+ }
+ }
+ }
+}
diff --git a/apps/theming/tests/Themes/DarkHighContrastThemeTest.php b/apps/theming/tests/Themes/DarkHighContrastThemeTest.php
new file mode 100644
index 00000000000..d03e8b13300
--- /dev/null
+++ b/apps/theming/tests/Themes/DarkHighContrastThemeTest.php
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Themes;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\BackgroundService;
+use OCA\Theming\Themes\DarkHighContrastTheme;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\Files\IAppData;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class DarkHighContrastThemeTest extends AccessibleThemeTestCase {
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IUserSession&MockObject $userSession;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ImageManager&MockObject $imageManager;
+ private IConfig&MockObject $config;
+ private IL10N&MockObject $l10n;
+ private IAppManager&MockObject $appManager;
+
+ // !! important: Enable WCAG AAA tests
+ protected static bool $WCAGaaa = true;
+
+ protected function setUp(): void {
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->imageManager = $this->createMock(ImageManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+
+ $this->util = new Util(
+ $this->createMock(ServerVersion::class),
+ $this->config,
+ $this->appManager,
+ $this->createMock(IAppData::class),
+ $this->imageManager
+ );
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn('#0082c9');
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorBackground')
+ ->willReturn('#0082c9');
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorBackground')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getBackground')
+ ->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
+
+ $this->l10n
+ ->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($text, $parameters = []) {
+ return vsprintf($text, $parameters);
+ });
+
+ $this->urlGenerator
+ ->expects($this->any())
+ ->method('imagePath')
+ ->willReturnCallback(function ($app = 'core', $filename = '') {
+ return "/$app/img/$filename";
+ });
+
+ $this->theme = new DarkHighContrastTheme(
+ $this->util,
+ $this->themingDefaults,
+ $this->userSession,
+ $this->urlGenerator,
+ $this->imageManager,
+ $this->config,
+ $this->l10n,
+ $this->appManager,
+ null,
+ );
+
+ parent::setUp();
+ }
+
+
+ public function testGetId(): void {
+ $this->assertEquals('dark-highcontrast', $this->theme->getId());
+ }
+
+ public function testGetType(): void {
+ $this->assertEquals(ITheme::TYPE_THEME, $this->theme->getType());
+ }
+
+ public function testGetTitle(): void {
+ $this->assertEquals('Dark theme with high contrast mode', $this->theme->getTitle());
+ }
+
+ public function testGetEnableLabel(): void {
+ $this->assertEquals('Enable dark high contrast mode', $this->theme->getEnableLabel());
+ }
+
+ public function testGetDescription(): void {
+ $this->assertEquals('Similar to the high contrast mode, but with dark colours.', $this->theme->getDescription());
+ }
+
+ public function testGetMediaQuery(): void {
+ $this->assertEquals('(prefers-color-scheme: dark) and (prefers-contrast: more)', $this->theme->getMediaQuery());
+ }
+}
diff --git a/apps/theming/tests/Themes/DarkThemeTest.php b/apps/theming/tests/Themes/DarkThemeTest.php
new file mode 100644
index 00000000000..656779b5b24
--- /dev/null
+++ b/apps/theming/tests/Themes/DarkThemeTest.php
@@ -0,0 +1,132 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Themes;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\BackgroundService;
+use OCA\Theming\Themes\DarkTheme;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\Files\IAppData;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class DarkThemeTest extends AccessibleThemeTestCase {
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IUserSession&MockObject $userSession;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ImageManager&MockObject $imageManager;
+ private IConfig&MockObject $config;
+ private IL10N&MockObject $l10n;
+ private IAppManager&MockObject $appManager;
+
+ protected function setUp(): void {
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->imageManager = $this->createMock(ImageManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+
+ $this->util = new Util(
+ $this->createMock(ServerVersion::class),
+ $this->config,
+ $this->appManager,
+ $this->createMock(IAppData::class),
+ $this->imageManager
+ );
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn('#0082c9');
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorBackground')
+ ->willReturn('#0082c9');
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorBackground')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getBackground')
+ ->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
+
+ $this->l10n
+ ->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($text, $parameters = []) {
+ return vsprintf($text, $parameters);
+ });
+
+ $this->urlGenerator
+ ->expects($this->any())
+ ->method('imagePath')
+ ->willReturnCallback(function ($app = 'core', $filename = '') {
+ return "/$app/img/$filename";
+ });
+
+ $this->theme = new DarkTheme(
+ $this->util,
+ $this->themingDefaults,
+ $this->userSession,
+ $this->urlGenerator,
+ $this->imageManager,
+ $this->config,
+ $this->l10n,
+ $this->appManager,
+ null,
+ );
+
+ parent::setUp();
+ }
+
+
+ public function testGetId(): void {
+ $this->assertEquals('dark', $this->theme->getId());
+ }
+
+ public function testGetType(): void {
+ $this->assertEquals(ITheme::TYPE_THEME, $this->theme->getType());
+ }
+
+ public function testGetTitle(): void {
+ $this->assertEquals('Dark theme', $this->theme->getTitle());
+ }
+
+ public function testGetEnableLabel(): void {
+ $this->assertEquals('Enable dark theme', $this->theme->getEnableLabel());
+ }
+
+ public function testGetDescription(): void {
+ $this->assertEquals('A dark theme to ease your eyes by reducing the overall luminosity and brightness.', $this->theme->getDescription());
+ }
+
+ public function testGetMediaQuery(): void {
+ $this->assertEquals('(prefers-color-scheme: dark)', $this->theme->getMediaQuery());
+ }
+
+ public function testGetCustomCss(): void {
+ $this->assertEquals('', $this->theme->getCustomCss());
+ }
+}
diff --git a/apps/theming/tests/Themes/DefaultThemeTest.php b/apps/theming/tests/Themes/DefaultThemeTest.php
new file mode 100644
index 00000000000..d2606ffc275
--- /dev/null
+++ b/apps/theming/tests/Themes/DefaultThemeTest.php
@@ -0,0 +1,157 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Themes;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\BackgroundService;
+use OCA\Theming\Themes\DefaultTheme;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\Files\IAppData;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class DefaultThemeTest extends AccessibleThemeTestCase {
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IUserSession&MockObject $userSession;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ImageManager&MockObject $imageManager;
+ private IConfig&MockObject $config;
+ private IL10N&MockObject $l10n;
+ private IAppManager&MockObject $appManager;
+
+ protected function setUp(): void {
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->imageManager = $this->createMock(ImageManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+
+ $this->util = new Util(
+ $this->createMock(ServerVersion::class),
+ $this->config,
+ $this->appManager,
+ $this->createMock(IAppData::class),
+ $this->imageManager
+ );
+
+ $defaultBackground = BackgroundService::SHIPPED_BACKGROUNDS[BackgroundService::DEFAULT_BACKGROUND_IMAGE];
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn($defaultBackground['primary_color']);
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorBackground')
+ ->willReturn($defaultBackground['background_color']);
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn($defaultBackground['primary_color']);
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorBackground')
+ ->willReturn($defaultBackground['background_color']);
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getBackground')
+ ->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
+
+ $this->l10n
+ ->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($text, $parameters = []) {
+ return vsprintf($text, $parameters);
+ });
+
+ $this->urlGenerator
+ ->expects($this->any())
+ ->method('imagePath')
+ ->willReturnCallback(function ($app = 'core', $filename = '') {
+ return "/$app/img/$filename";
+ });
+
+ $this->theme = new DefaultTheme(
+ $this->util,
+ $this->themingDefaults,
+ $this->userSession,
+ $this->urlGenerator,
+ $this->imageManager,
+ $this->config,
+ $this->l10n,
+ $this->appManager,
+ null,
+ );
+
+ parent::setUp();
+ }
+
+
+ public function testGetId(): void {
+ $this->assertEquals('default', $this->theme->getId());
+ }
+
+ public function testGetType(): void {
+ $this->assertEquals(ITheme::TYPE_THEME, $this->theme->getType());
+ }
+
+ public function testGetTitle(): void {
+ $this->assertEquals('System default theme', $this->theme->getTitle());
+ }
+
+ public function testGetEnableLabel(): void {
+ $this->assertEquals('Enable the system default', $this->theme->getEnableLabel());
+ }
+
+ public function testGetDescription(): void {
+ $this->assertEquals('Using the default system appearance.', $this->theme->getDescription());
+ }
+
+ public function testGetMediaQuery(): void {
+ $this->assertEquals('', $this->theme->getMediaQuery());
+ }
+
+ public function testGetCustomCss(): void {
+ $this->assertEquals('', $this->theme->getCustomCss());
+ }
+
+ /**
+ * Ensure parity between the default theme and the static generated file
+ * @see ThemingController.php:313
+ */
+ public function testThemindDisabledFallbackCss(): void {
+ // Generate variables
+ $variables = '';
+ foreach ($this->theme->getCSSVariables() as $variable => $value) {
+ $variables .= " $variable: $value;" . PHP_EOL;
+ };
+
+ $css = "\n:root {" . PHP_EOL . "$variables}" . PHP_EOL;
+ $fallbackCss = file_get_contents(__DIR__ . '/../../css/default.css');
+ // Remove comments
+ $fallbackCss = preg_replace('/\s*\/\*[\s\S]*?\*\//m', '', $fallbackCss);
+ // Remove blank lines
+ $fallbackCss = preg_replace('/\s*\n\n/', "\n", $fallbackCss);
+
+ $this->assertEquals($css, $fallbackCss);
+ }
+}
diff --git a/apps/theming/tests/Themes/DyslexiaFontTest.php b/apps/theming/tests/Themes/DyslexiaFontTest.php
new file mode 100644
index 00000000000..7d56fb4b1be
--- /dev/null
+++ b/apps/theming/tests/Themes/DyslexiaFontTest.php
@@ -0,0 +1,163 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Service;
+
+use OC\Route\Router;
+use OC\URLGenerator;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Themes\DyslexiaFont;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\Files\IAppData;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class DyslexiaFontTest extends TestCase {
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IUserSession&MockObject $userSession;
+ private IURLGenerator $urlGenerator;
+ private ImageManager&MockObject $imageManager;
+ private IConfig&MockObject $config;
+ private IL10N&MockObject $l10n;
+ private IAppManager&MockObject $appManager;
+
+ private DyslexiaFont $dyslexiaFont;
+
+ protected function setUp(): void {
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->imageManager = $this->createMock(ImageManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+
+ $util = new Util(
+ $this->createMock(ServerVersion::class),
+ $this->config,
+ $this->appManager,
+ $this->createMock(IAppData::class),
+ $this->imageManager
+ );
+
+ $userSession = $this->createMock(IUserSession::class);
+ $cacheFactory = $this->createMock(ICacheFactory::class);
+ $request = $this->createMock(IRequest::class);
+ $router = $this->createMock(Router::class);
+ $this->urlGenerator = new URLGenerator(
+ $this->config,
+ $userSession,
+ $cacheFactory,
+ $request,
+ $router
+ );
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorBackground')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorBackground')
+ ->willReturn('#0082c9');
+
+ $this->l10n
+ ->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($text, $parameters = []) {
+ return vsprintf($text, $parameters);
+ });
+
+ $this->dyslexiaFont = new DyslexiaFont(
+ $util,
+ $this->themingDefaults,
+ $this->userSession,
+ $this->urlGenerator,
+ $this->imageManager,
+ $this->config,
+ $this->l10n,
+ $this->appManager,
+ null,
+ );
+
+ parent::setUp();
+ }
+
+
+ public function testGetId(): void {
+ $this->assertEquals('opendyslexic', $this->dyslexiaFont->getId());
+ }
+
+ public function testGetType(): void {
+ $this->assertEquals(ITheme::TYPE_FONT, $this->dyslexiaFont->getType());
+ }
+
+ public function testGetTitle(): void {
+ $this->assertNotEmpty($this->dyslexiaFont->getTitle());
+ }
+
+ public function testGetEnableLabel(): void {
+ $this->assertNotEmpty($this->dyslexiaFont->getEnableLabel());
+ }
+
+ public function testGetDescription(): void {
+ $this->assertNotEmpty($this->dyslexiaFont->getDescription());
+ }
+
+ public function testGetMediaQuery(): void {
+ $this->assertEquals('', $this->dyslexiaFont->getMediaQuery());
+ }
+
+ public function testGetCSSVariables(): void {
+ $this->assertStringStartsWith('OpenDyslexic', $this->dyslexiaFont->getCSSVariables()['--font-face']);
+ }
+
+ public static function dataTestGetCustomCss(): array {
+ return [
+ ['', true],
+ ['', false],
+ ['/subfolder', true],
+ ['/subfolder', false],
+ ];
+ }
+
+ /**
+ * Ensure the fonts are always loaded from the web root
+ * despite having url rewriting enabled or not
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetCustomCss')]
+ public function testGetCustomCss(string $webRoot, bool $prettyUrlsEnabled): void {
+ \OC::$WEBROOT = $webRoot;
+ $this->config->expects($this->any())
+ ->method('getSystemValue')
+ ->with('htaccess.IgnoreFrontController', false)
+ ->willReturn($prettyUrlsEnabled);
+
+ $this->assertStringContainsString("'$webRoot/apps/theming/fonts/OpenDyslexic-Regular.otf'", $this->dyslexiaFont->getCustomCss());
+ $this->assertStringNotContainsString('index.php', $this->dyslexiaFont->getCustomCss());
+ }
+}
diff --git a/apps/theming/tests/Themes/HighContrastThemeTest.php b/apps/theming/tests/Themes/HighContrastThemeTest.php
new file mode 100644
index 00000000000..94f87d7433b
--- /dev/null
+++ b/apps/theming/tests/Themes/HighContrastThemeTest.php
@@ -0,0 +1,131 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Theming\Tests\Themes;
+
+use OCA\Theming\AppInfo\Application;
+use OCA\Theming\ImageManager;
+use OCA\Theming\ITheme;
+use OCA\Theming\Service\BackgroundService;
+use OCA\Theming\Themes\HighContrastTheme;
+use OCA\Theming\ThemingDefaults;
+use OCA\Theming\Util;
+use OCP\App\IAppManager;
+use OCP\Files\IAppData;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class HighContrastThemeTest extends AccessibleThemeTestCase {
+ private ThemingDefaults&MockObject $themingDefaults;
+ private IUserSession&MockObject $userSession;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ImageManager&MockObject $imageManager;
+ private IConfig&MockObject $config;
+ private IL10N&MockObject $l10n;
+ private IAppManager&MockObject $appManager;
+
+ // !! important: Enable WCAG AAA tests
+ protected static bool $WCAGaaa = true;
+
+ protected function setUp(): void {
+ $this->themingDefaults = $this->createMock(ThemingDefaults::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->imageManager = $this->createMock(ImageManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+
+ $this->util = new Util(
+ $this->createMock(ServerVersion::class),
+ $this->config,
+ $this->appManager,
+ $this->createMock(IAppData::class),
+ $this->imageManager
+ );
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorPrimary')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorPrimary')
+ ->willReturn('#0082c9');
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getColorBackground')
+ ->willReturn('#0082c9');
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getDefaultColorBackground')
+ ->willReturn('#0082c9');
+
+ $this->themingDefaults
+ ->expects($this->any())
+ ->method('getBackground')
+ ->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
+
+ $this->l10n
+ ->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($text, $parameters = []) {
+ return vsprintf($text, $parameters);
+ });
+
+ $this->urlGenerator
+ ->expects($this->any())
+ ->method('imagePath')
+ ->willReturnCallback(function ($app = 'core', $filename = '') {
+ return "/$app/img/$filename";
+ });
+
+ $this->theme = new HighContrastTheme(
+ $this->util,
+ $this->themingDefaults,
+ $this->userSession,
+ $this->urlGenerator,
+ $this->imageManager,
+ $this->config,
+ $this->l10n,
+ $this->appManager,
+ null,
+ );
+
+ parent::setUp();
+ }
+
+
+ public function testGetId(): void {
+ $this->assertEquals('light-highcontrast', $this->theme->getId());
+ }
+
+ public function testGetType(): void {
+ $this->assertEquals(ITheme::TYPE_THEME, $this->theme->getType());
+ }
+
+ public function testGetTitle(): void {
+ $this->assertEquals('High contrast mode', $this->theme->getTitle());
+ }
+
+ public function testGetEnableLabel(): void {
+ $this->assertEquals('Enable high contrast mode', $this->theme->getEnableLabel());
+ }
+
+ public function testGetDescription(): void {
+ $this->assertEquals('A high contrast mode to ease your navigation. Visual quality will be reduced but clarity will be increased.', $this->theme->getDescription());
+ }
+
+ public function testGetMediaQuery(): void {
+ $this->assertEquals('(prefers-contrast: more)', $this->theme->getMediaQuery());
+ }
+}
diff --git a/apps/theming/tests/ThemingDefaultsTest.php b/apps/theming/tests/ThemingDefaultsTest.php
index 4db5dad2c0d..1acd12f12fa 100644
--- a/apps/theming/tests/ThemingDefaultsTest.php
+++ b/apps/theming/tests/ThemingDefaultsTest.php
@@ -1,84 +1,52 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Guillaume COMPAGNON <gcompagnon@outlook.com>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
- * @author Julius Haertl <jus@bitgrid.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Michael Weimann <mail@michael-weimann.eu>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests;
use OCA\Theming\ImageManager;
+use OCA\Theming\Service\BackgroundService;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
use OCP\App\IAppManager;
-use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
+use OCP\IAppConfig;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class ThemingDefaultsTest extends TestCase {
- /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
- private $config;
- /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
- private $l10n;
- /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
- private $urlGenerator;
- /** @var \OC_Defaults|\PHPUnit\Framework\MockObject\MockObject */
- private $defaults;
- /** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */
- private $appData;
- /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
- private $cacheFactory;
- /** @var ThemingDefaults */
- private $template;
- /** @var Util|\PHPUnit\Framework\MockObject\MockObject */
- private $util;
- /** @var ICache|\PHPUnit\Framework\MockObject\MockObject */
- private $cache;
- /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
- private $appManager;
- /** @var ImageManager|\PHPUnit\Framework\MockObject\MockObject */
- private $imageManager;
- /** @var INavigationManager|\PHPUnit\Framework\MockObject\MockObject */
- private $navigationManager;
+ private IAppConfig&MockObject $appConfig;
+ private IConfig&MockObject $config;
+ private \OC_Defaults $defaults;
+ private IL10N|MockObject $l10n;
+ private IUserSession&MockObject $userSession;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ICacheFactory&MockObject $cacheFactory;
+ private Util&MockObject $util;
+ private ICache&MockObject $cache;
+ private IAppManager&MockObject $appManager;
+ private ImageManager&MockObject $imageManager;
+ private INavigationManager&MockObject $navigationManager;
+ private BackgroundService&MockObject $backgroundService;
+ private ThemingDefaults $template;
protected function setUp(): void {
parent::setUp();
+ $this->appConfig = $this->createMock(IAppConfig::class);
$this->config = $this->createMock(IConfig::class);
$this->l10n = $this->createMock(IL10N::class);
+ $this->userSession = $this->createMock(IUserSession::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->cache = $this->createMock(ICache::class);
@@ -86,6 +54,7 @@ class ThemingDefaultsTest extends TestCase {
$this->imageManager = $this->createMock(ImageManager::class);
$this->appManager = $this->createMock(IAppManager::class);
$this->navigationManager = $this->createMock(INavigationManager::class);
+ $this->backgroundService = $this->createMock(BackgroundService::class);
$this->defaults = new \OC_Defaults();
$this->urlGenerator
->expects($this->any())
@@ -93,17 +62,20 @@ class ThemingDefaultsTest extends TestCase {
->willReturn('');
$this->template = new ThemingDefaults(
$this->config,
+ $this->appConfig,
$this->l10n,
+ $this->userSession,
$this->urlGenerator,
$this->cacheFactory,
$this->util,
$this->imageManager,
$this->appManager,
- $this->navigationManager
+ $this->navigationManager,
+ $this->backgroundService,
);
}
- public function testGetNameWithDefault() {
+ public function testGetNameWithDefault(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -113,7 +85,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('Nextcloud', $this->template->getName());
}
- public function testGetNameWithCustom() {
+ public function testGetNameWithCustom(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -123,7 +95,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('MyCustomCloud', $this->template->getName());
}
- public function testGetHTMLNameWithDefault() {
+ public function testGetHTMLNameWithDefault(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -133,7 +105,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('Nextcloud', $this->template->getHTMLName());
}
- public function testGetHTMLNameWithCustom() {
+ public function testGetHTMLNameWithCustom(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -143,7 +115,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('MyCustomCloud', $this->template->getHTMLName());
}
- public function testGetTitleWithDefault() {
+ public function testGetTitleWithDefault(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -153,7 +125,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('Nextcloud', $this->template->getTitle());
}
- public function testGetTitleWithCustom() {
+ public function testGetTitleWithCustom(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -164,7 +136,7 @@ class ThemingDefaultsTest extends TestCase {
}
- public function testGetEntityWithDefault() {
+ public function testGetEntityWithDefault(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -174,7 +146,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('Nextcloud', $this->template->getEntity());
}
- public function testGetEntityWithCustom() {
+ public function testGetEntityWithCustom(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -184,7 +156,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('MyCustomCloud', $this->template->getEntity());
}
- public function testGetBaseUrlWithDefault() {
+ public function testGetBaseUrlWithDefault(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -194,7 +166,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals($this->defaults->getBaseUrl(), $this->template->getBaseUrl());
}
- public function testGetBaseUrlWithCustom() {
+ public function testGetBaseUrlWithCustom(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -204,18 +176,15 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('https://example.com/', $this->template->getBaseUrl());
}
- public function legalUrlProvider() {
+ public static function legalUrlProvider(): array {
return [
- [ '' ],
- [ 'https://example.com/legal.html']
+ [''],
+ ['https://example.com/legal.html'],
];
}
- /**
- * @param $imprintUrl
- * @dataProvider legalUrlProvider
- */
- public function testGetImprintURL($imprintUrl) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('legalUrlProvider')]
+ public function testGetImprintURL(string $imprintUrl): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -225,11 +194,8 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals($imprintUrl, $this->template->getImprintUrl());
}
- /**
- * @param $privacyUrl
- * @dataProvider legalUrlProvider
- */
- public function testGetPrivacyURL($privacyUrl) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('legalUrlProvider')]
+ public function testGetPrivacyURL(string $privacyUrl): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -239,7 +205,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals($privacyUrl, $this->template->getPrivacyUrl());
}
- public function testGetSloganWithDefault() {
+ public function testGetSloganWithDefault(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -249,7 +215,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals($this->defaults->getSlogan(), $this->template->getSlogan());
}
- public function testGetSloganWithCustom() {
+ public function testGetSloganWithCustom(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -259,7 +225,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('My custom Slogan', $this->template->getSlogan());
}
- public function testGetShortFooter() {
+ public function testGetShortFooter(): void {
$this->config
->expects($this->exactly(5))
->method('getAppValue')
@@ -274,7 +240,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
}
- public function testGetShortFooterEmptyUrl() {
+ public function testGetShortFooterEmptyUrl(): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -290,7 +256,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('<span class="entity-name">Name</span> – Slogan', $this->template->getShortFooter());
}
- public function testGetShortFooterEmptySlogan() {
+ public function testGetShortFooterEmptySlogan(): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -306,7 +272,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a>', $this->template->getShortFooter());
}
- public function testGetShortFooterImprint() {
+ public function testGetShortFooterImprint(): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -324,10 +290,10 @@ class ThemingDefaultsTest extends TestCase {
->method('t')
->willReturnArgument(0);
- $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a>', $this->template->getShortFooter());
+ $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a></span>', $this->template->getShortFooter());
}
- public function testGetShortFooterPrivacy() {
+ public function testGetShortFooterPrivacy(): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -345,10 +311,10 @@ class ThemingDefaultsTest extends TestCase {
->method('t')
->willReturnArgument(0);
- $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a>', $this->template->getShortFooter());
+ $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a></span>', $this->template->getShortFooter());
}
- public function testGetShortFooterAllLegalLinks() {
+ public function testGetShortFooterAllLegalLinks(): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -366,21 +332,18 @@ class ThemingDefaultsTest extends TestCase {
->method('t')
->willReturnArgument(0);
- $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a> · <a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a>', $this->template->getShortFooter());
+ $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a> · <a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a></span>', $this->template->getShortFooter());
}
- public function invalidLegalUrlProvider() {
+ public static function invalidLegalUrlProvider(): array {
return [
['example.com/legal'], # missing scheme
['https:///legal'], # missing host
];
}
- /**
- * @param $invalidImprintUrl
- * @dataProvider invalidLegalUrlProvider
- */
- public function testGetShortFooterInvalidImprint($invalidImprintUrl) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('invalidLegalUrlProvider')]
+ public function testGetShortFooterInvalidImprint(string $invalidImprintUrl): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -396,11 +359,8 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
}
- /**
- * @param $invalidPrivacyUrl
- * @dataProvider invalidLegalUrlProvider
- */
- public function testGetShortFooterInvalidPrivacy($invalidPrivacyUrl) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('invalidLegalUrlProvider')]
+ public function testGetShortFooterInvalidPrivacy(string $invalidPrivacyUrl): void {
$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
$this->config
->expects($this->exactly(5))
@@ -416,50 +376,130 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
}
- public function testgetColorPrimaryWithDefault() {
- $this->config
- ->expects($this->once())
- ->method('getAppValue')
- ->with('theming', 'color', $this->defaults->getColorPrimary())
+ public function testGetColorPrimaryWithDefault(): void {
+ $this->appConfig
+ ->expects(self::once())
+ ->method('getValueBool')
+ ->with('theming', 'disable-user-theming')
+ ->willReturn(false);
+ $this->appConfig
+ ->expects(self::once())
+ ->method('getValueString')
+ ->with('theming', 'primary_color', '')
->willReturn($this->defaults->getColorPrimary());
$this->assertEquals($this->defaults->getColorPrimary(), $this->template->getColorPrimary());
}
- public function testgetColorPrimaryWithCustom() {
- $this->config
- ->expects($this->once())
- ->method('getAppValue')
- ->with('theming', 'color', $this->defaults->getColorPrimary())
+ public function testGetColorPrimaryWithCustom(): void {
+ $this->appConfig
+ ->expects(self::once())
+ ->method('getValueBool')
+ ->with('theming', 'disable-user-theming')
+ ->willReturn(false);
+ $this->appConfig
+ ->expects(self::once())
+ ->method('getValueString')
+ ->with('theming', 'primary_color', '')
->willReturn('#fff');
$this->assertEquals('#fff', $this->template->getColorPrimary());
}
- public function testSet() {
+ public static function dataGetColorPrimary(): array {
+ return [
+ 'with fallback default' => [
+ 'disableTheming' => false,
+ 'primaryColor' => '',
+ 'userPrimaryColor' => '',
+ 'expected' => BackgroundService::DEFAULT_COLOR,
+ ],
+ 'with custom admin primary' => [
+ 'disableTheming' => false,
+ 'primaryColor' => '#aaa',
+ 'userPrimaryColor' => '',
+ 'expected' => '#aaa',
+ ],
+ 'with custom invalid admin primary' => [
+ 'disableTheming' => false,
+ 'primaryColor' => 'invalid',
+ 'userPrimaryColor' => '',
+ 'expected' => BackgroundService::DEFAULT_COLOR,
+ ],
+ 'with custom invalid user primary' => [
+ 'disableTheming' => false,
+ 'primaryColor' => '',
+ 'userPrimaryColor' => 'invalid-name',
+ 'expected' => BackgroundService::DEFAULT_COLOR,
+ ],
+ 'with custom user primary' => [
+ 'disableTheming' => false,
+ 'primaryColor' => '',
+ 'userPrimaryColor' => '#bbb',
+ 'expected' => '#bbb',
+ ],
+ 'with disabled user theming primary' => [
+ 'disableTheming' => true,
+ 'primaryColor' => '#aaa',
+ 'userPrimaryColor' => '#bbb',
+ 'expected' => '#aaa',
+ ],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetColorPrimary')]
+ public function testGetColorPrimary(bool $disableTheming, string $primaryColor, string $userPrimaryColor, string $expected): void {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user');
+ $this->appConfig
+ ->expects(self::any())
+ ->method('getValueBool')
+ ->with('theming', 'disable-user-theming')
+ ->willReturn($disableTheming);
+ $this->appConfig
+ ->expects(self::any())
+ ->method('getValueString')
+ ->with('theming', 'primary_color', '')
+ ->willReturn($primaryColor);
$this->config
- ->expects($this->at(0))
+ ->expects($this->any())
+ ->method('getUserValue')
+ ->with('user', 'theming', 'primary_color', '')
+ ->willReturn($userPrimaryColor);
+
+ $this->assertEquals($expected, $this->template->getColorPrimary());
+ }
+
+ public function testSet(): void {
+ $expectedCalls = [
+ ['theming', 'MySetting', 'MyValue'],
+ ['theming', 'cachebuster', 16],
+ ];
+ $i = 0;
+ $this->config
+ ->expects($this->exactly(2))
->method('setAppValue')
- ->with('theming', 'MySetting', 'MyValue');
+ ->willReturnCallback(function () use ($expectedCalls, &$i): void {
+ $this->assertEquals($expectedCalls[$i], func_get_args());
+ $i++;
+ });
$this->config
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getAppValue')
->with('theming', 'cachebuster', '0')
->willReturn('15');
- $this->config
- ->expects($this->at(2))
- ->method('setAppValue')
- ->with('theming', 'cachebuster', 16);
$this->cacheFactory
- ->expects($this->at(0))
+ ->expects($this->exactly(2))
->method('createDistributed')
- ->with('theming-')
- ->willReturn($this->cache);
- $this->cacheFactory
- ->expects($this->at(1))
- ->method('createDistributed')
- ->with('imagePath')
- ->willReturn($this->cache);
+ ->willReturnMap([
+ ['theming-', $this->cache],
+ ['imagePath', $this->cache],
+ ]);
$this->cache
->expects($this->any())
->method('clear')
@@ -467,117 +507,103 @@ class ThemingDefaultsTest extends TestCase {
$this->template->set('MySetting', 'MyValue');
}
- public function testUndoName() {
+ public function testUndoName(): void {
$this->config
- ->expects($this->at(0))
+ ->expects($this->once())
->method('deleteAppValue')
->with('theming', 'name');
$this->config
- ->expects($this->at(1))
+ ->expects($this->exactly(2))
->method('getAppValue')
- ->with('theming', 'cachebuster', '0')
- ->willReturn('15');
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '15'],
+ ['theming', 'name', 'Nextcloud', 'Nextcloud'],
+ ]);
$this->config
- ->expects($this->at(2))
+ ->expects($this->once())
->method('setAppValue')
->with('theming', 'cachebuster', 16);
- $this->config
- ->expects($this->at(3))
- ->method('getAppValue')
- ->with('theming', 'name', 'Nextcloud')
- ->willReturn('Nextcloud');
$this->assertSame('Nextcloud', $this->template->undo('name'));
}
- public function testUndoBaseUrl() {
+ public function testUndoBaseUrl(): void {
$this->config
- ->expects($this->at(0))
+ ->expects($this->once())
->method('deleteAppValue')
->with('theming', 'url');
$this->config
- ->expects($this->at(1))
+ ->expects($this->exactly(2))
->method('getAppValue')
- ->with('theming', 'cachebuster', '0')
- ->willReturn('15');
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '15'],
+ ['theming', 'url', $this->defaults->getBaseUrl(), $this->defaults->getBaseUrl()],
+ ]);
$this->config
- ->expects($this->at(2))
+ ->expects($this->once())
->method('setAppValue')
->with('theming', 'cachebuster', 16);
- $this->config
- ->expects($this->at(3))
- ->method('getAppValue')
- ->with('theming', 'url', $this->defaults->getBaseUrl())
- ->willReturn($this->defaults->getBaseUrl());
$this->assertSame($this->defaults->getBaseUrl(), $this->template->undo('url'));
}
- public function testUndoSlogan() {
+ public function testUndoSlogan(): void {
$this->config
- ->expects($this->at(0))
+ ->expects($this->once())
->method('deleteAppValue')
->with('theming', 'slogan');
$this->config
- ->expects($this->at(1))
+ ->expects($this->exactly(2))
->method('getAppValue')
- ->with('theming', 'cachebuster', '0')
- ->willReturn('15');
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '15'],
+ ['theming', 'slogan', $this->defaults->getSlogan(), $this->defaults->getSlogan()],
+ ]);
$this->config
- ->expects($this->at(2))
+ ->expects($this->once())
->method('setAppValue')
->with('theming', 'cachebuster', 16);
- $this->config
- ->expects($this->at(3))
- ->method('getAppValue')
- ->with('theming', 'slogan', $this->defaults->getSlogan())
- ->willReturn($this->defaults->getSlogan());
$this->assertSame($this->defaults->getSlogan(), $this->template->undo('slogan'));
}
- public function testUndoColor() {
+ public function testUndoPrimaryColor(): void {
$this->config
- ->expects($this->at(0))
+ ->expects($this->once())
->method('deleteAppValue')
- ->with('theming', 'color');
+ ->with('theming', 'primary_color');
$this->config
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getAppValue')
->with('theming', 'cachebuster', '0')
->willReturn('15');
$this->config
- ->expects($this->at(2))
+ ->expects($this->once())
->method('setAppValue')
->with('theming', 'cachebuster', 16);
- $this->config
- ->expects($this->at(3))
- ->method('getAppValue')
- ->with('theming', 'color', $this->defaults->getColorPrimary())
- ->willReturn($this->defaults->getColorPrimary());
- $this->assertSame($this->defaults->getColorPrimary(), $this->template->undo('color'));
+ $this->assertSame($this->defaults->getColorPrimary(), $this->template->undo('primary_color'));
}
- public function testUndoDefaultAction() {
+ public function testUndoDefaultAction(): void {
$this->config
- ->expects($this->at(0))
+ ->expects($this->once())
->method('deleteAppValue')
->with('theming', 'defaultitem');
$this->config
- ->expects($this->at(1))
+ ->expects($this->once())
->method('getAppValue')
->with('theming', 'cachebuster', '0')
->willReturn('15');
$this->config
- ->expects($this->at(2))
+ ->expects($this->once())
->method('setAppValue')
->with('theming', 'cachebuster', 16);
$this->assertSame('', $this->template->undo('defaultitem'));
}
- public function testGetBackground() {
+ public function testGetBackground(): void {
$this->imageManager
->expects($this->once())
->method('getImageUrl')
@@ -592,15 +618,12 @@ class ThemingDefaultsTest extends TestCase {
->with('logo')
->willThrowException(new NotFoundException());
$this->config
- ->expects($this->at(0))
+ ->expects($this->exactly(2))
->method('getAppValue')
- ->with('theming', 'logoMime')
- ->willReturn('');
- $this->config
- ->expects($this->at(1))
- ->method('getAppValue')
- ->with('theming', 'cachebuster', '0')
- ->willReturn('0');
+ ->willReturnMap([
+ ['theming', 'logoMime', '', ''],
+ ['theming', 'cachebuster', '0', '0'],
+ ]);
$this->urlGenerator->expects($this->once())
->method('imagePath')
->with('core', $withName)
@@ -608,25 +631,22 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('core-logo?v=0', $this->template->getLogo($useSvg));
}
- public function testGetLogoDefaultWithSvg() {
+ public function testGetLogoDefaultWithSvg(): void {
$this->getLogoHelper('logo/logo.svg', true);
}
- public function testGetLogoDefaultWithoutSvg() {
+ public function testGetLogoDefaultWithoutSvg(): void {
$this->getLogoHelper('logo/logo.png', false);
}
- public function testGetLogoCustom() {
+ public function testGetLogoCustom(): void {
$this->config
- ->expects($this->at(0))
+ ->expects($this->exactly(2))
->method('getAppValue')
- ->with('theming', 'logoMime', false)
- ->willReturn('image/svg+xml');
- $this->config
- ->expects($this->at(1))
- ->method('getAppValue')
- ->with('theming', 'cachebuster', '0')
- ->willReturn('0');
+ ->willReturnMap([
+ ['theming', 'logoMime', '', 'image/svg+xml'],
+ ['theming', 'cachebuster', '0', '0'],
+ ]);
$this->urlGenerator->expects($this->once())
->method('linkToRoute')
->with('theming.Theming.getImage')
@@ -634,7 +654,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('custom-logo' . '?v=0', $this->template->getLogo());
}
- public function testGetScssVariablesCached() {
+ public function testGetScssVariablesCached(): void {
$this->config->expects($this->any())->method('getAppValue')->with('theming', 'cachebuster', '0')->willReturn('1');
$this->cacheFactory->expects($this->once())
->method('createDistributed')
@@ -644,17 +664,25 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals(['foo' => 'bar'], $this->template->getScssVariables());
}
- public function testGetScssVariables() {
- $this->config->expects($this->at(0))->method('getAppValue')->with('theming', 'cachebuster', '0')->willReturn('0');
- $this->config->expects($this->at(1))->method('getAppValue')->with('theming', 'logoMime', false)->willReturn('jpeg');
- $this->config->expects($this->at(2))->method('getAppValue')->with('theming', 'backgroundMime', false)->willReturn('jpeg');
- $this->config->expects($this->at(3))->method('getAppValue')->with('theming', 'logoheaderMime', false)->willReturn('jpeg');
- $this->config->expects($this->at(4))->method('getAppValue')->with('theming', 'faviconMime', false)->willReturn('jpeg');
+ public function testGetScssVariables(): void {
+ $this->config
+ ->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['theming', 'cachebuster', '0', '0'],
+ ['theming', 'logoMime', '', 'jpeg'],
+ ['theming', 'backgroundMime', '', 'jpeg'],
+ ['theming', 'logoheaderMime', '', 'jpeg'],
+ ['theming', 'faviconMime', '', 'jpeg'],
+ ]);
- $this->config->expects($this->at(5))->method('getAppValue')->with('theming', 'color', null)->willReturn($this->defaults->getColorPrimary());
- $this->config->expects($this->at(6))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary());
- $this->config->expects($this->at(7))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary());
- $this->config->expects($this->at(8))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary());
+ $this->appConfig
+ ->expects(self::atLeastOnce())
+ ->method('getValueString')
+ ->willReturnMap([
+ ['theming', 'primary_color', '', false, $this->defaults->getColorPrimary()],
+ ['theming', 'primary_color', $this->defaults->getColorPrimary(), false, $this->defaults->getColorPrimary()],
+ ]);
$this->util->expects($this->any())->method('invertTextColor')->with($this->defaults->getColorPrimary())->willReturn(false);
$this->util->expects($this->any())->method('elementColor')->with($this->defaults->getColorPrimary())->willReturn('#aaaaaa');
@@ -663,10 +691,14 @@ class ThemingDefaultsTest extends TestCase {
->with('theming-0-')
->willReturn($this->cache);
$this->cache->expects($this->once())->method('get')->with('getScssVariables')->willReturn(null);
- $this->imageManager->expects($this->at(0))->method('getImageUrl')->with('logo')->willReturn('custom-logo?v=0');
- $this->imageManager->expects($this->at(1))->method('getImageUrl')->with('logoheader')->willReturn('custom-logoheader?v=0');
- $this->imageManager->expects($this->at(2))->method('getImageUrl')->with('favicon')->willReturn('custom-favicon?v=0');
- $this->imageManager->expects($this->at(3))->method('getImageUrl')->with('background')->willReturn('custom-background?v=0');
+ $this->imageManager->expects($this->exactly(4))
+ ->method('getImageUrl')
+ ->willReturnMap([
+ ['logo', 'custom-logo?v=0'],
+ ['logoheader', 'custom-logoheader?v=0'],
+ ['favicon', 'custom-favicon?v=0'],
+ ['background', 'custom-background?v=0'],
+ ]);
$expected = [
'theming-cachebuster' => '\'0\'',
@@ -687,7 +719,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals($expected, $this->template->getScssVariables());
}
- public function testGetDefaultAndroidURL() {
+ public function testGetDefaultAndroidURL(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -697,7 +729,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('https://play.google.com/store/apps/details?id=com.nextcloud.client', $this->template->getAndroidClientUrl());
}
- public function testGetCustomAndroidURL() {
+ public function testGetCustomAndroidURL(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -707,7 +739,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('https://play.google.com/store/apps/details?id=com.mycloud.client', $this->template->getAndroidClientUrl());
}
- public function testGetDefaultiOSURL() {
+ public function testGetDefaultiOSURL(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -717,7 +749,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8', $this->template->getiOSClientUrl());
}
- public function testGetCustomiOSURL() {
+ public function testGetCustomiOSURL(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -727,7 +759,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('https://geo.itunes.apple.com/us/app/nextcloud/id1234567890?mt=8', $this->template->getiOSClientUrl());
}
- public function testGetDefaultiTunesAppId() {
+ public function testGetDefaultiTunesAppId(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -737,7 +769,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('1125420102', $this->template->getiTunesAppId());
}
- public function testGetCustomiTunesAppId() {
+ public function testGetCustomiTunesAppId(): void {
$this->config
->expects($this->once())
->method('getAppValue')
@@ -747,7 +779,7 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('1234567890', $this->template->getiTunesAppId());
}
- public function dataReplaceImagePath() {
+ public static function dataReplaceImagePath(): array {
return [
['core', 'test.png', false],
['core', 'manifest.json'],
@@ -756,8 +788,8 @@ class ThemingDefaultsTest extends TestCase {
];
}
- /** @dataProvider dataReplaceImagePath */
- public function testReplaceImagePath($app, $image, $result = 'themingRoute?v=0') {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataReplaceImagePath')]
+ public function testReplaceImagePath(string $app, string $image, string|bool $result = 'themingRoute?v=1234abcd'): void {
$this->cache->expects($this->any())
->method('get')
->with('shouldReplaceIcons')
@@ -771,6 +803,12 @@ class ThemingDefaultsTest extends TestCase {
->expects($this->any())
->method('linkToRoute')
->willReturn('themingRoute');
+ if ($result) {
+ $this->util
+ ->expects($this->once())
+ ->method('getCacheBuster')
+ ->willReturn('1234abcd');
+ }
$this->assertEquals($result, $this->template->replaceImagePath($app, $image));
}
}
diff --git a/apps/theming/tests/UtilTest.php b/apps/theming/tests/UtilTest.php
index d6fe318cbca..1e944027e32 100644
--- a/apps/theming/tests/UtilTest.php
+++ b/apps/theming/tests/UtilTest.php
@@ -1,33 +1,13 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Haertl <jus@bitgrid.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Michael Weimann <mail@michael-weimann.eu>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Theming\Tests;
+use OCA\Theming\ImageManager;
use OCA\Theming\Util;
use OCP\App\IAppManager;
use OCP\Files\IAppData;
@@ -35,124 +15,149 @@ use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
+use OCP\Server;
+use OCP\ServerVersion;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class UtilTest extends TestCase {
- /** @var Util */
- protected $util;
- /** @var IConfig */
- protected $config;
- /** @var IAppData */
- protected $appData;
- /** @var IAppManager */
- protected $appManager;
+ protected Util $util;
+ protected IConfig&MockObject $config;
+ protected IAppData&MockObject $appData;
+ protected IAppManager $appManager;
+ protected ImageManager&MockObject $imageManager;
protected function setUp(): void {
parent::setUp();
$this->config = $this->createMock(IConfig::class);
$this->appData = $this->createMock(IAppData::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->util = new Util($this->config, $this->appManager, $this->appData);
+ $this->appManager = Server::get(IAppManager::class);
+ $this->imageManager = $this->createMock(ImageManager::class);
+ $this->util = new Util($this->createMock(ServerVersion::class), $this->config, $this->appManager, $this->appData, $this->imageManager);
}
- public function dataInvertTextColor() {
+ public static function dataColorContrast(): array {
+ return [
+ ['#ffffff', '#FFFFFF', 1],
+ ['#000000', '#000000', 1],
+ ['#ffffff', '#000000', 21],
+ ['#000000', '#FFFFFF', 21],
+ ['#9E9E9E', '#353535', 4.578],
+ ['#353535', '#9E9E9E', 4.578],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataColorContrast')]
+ public function testColorContrast(string $color1, string $color2, int|float $contrast): void {
+ $this->assertEqualsWithDelta($contrast, $this->util->colorContrast($color1, $color2), .001);
+ }
+
+ public static function dataInvertTextColor(): array {
return [
['#ffffff', true],
['#000000', false],
- ['#0082C9', false],
+ ['#00679e', false],
['#ffff00', true],
];
}
- /**
- * @dataProvider dataInvertTextColor
- */
- public function testInvertTextColor($color, $expected) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataInvertTextColor')]
+ public function testInvertTextColor(string $color, bool $expected): void {
$invert = $this->util->invertTextColor($color);
$this->assertEquals($expected, $invert);
}
- public function testCalculateLuminanceLight() {
+ public function testCalculateLuminanceLight(): void {
$luminance = $this->util->calculateLuminance('#ffffff');
$this->assertEquals(1, $luminance);
}
- public function testCalculateLuminanceDark() {
+ public function testCalculateLuminanceDark(): void {
$luminance = $this->util->calculateLuminance('#000000');
$this->assertEquals(0, $luminance);
}
- public function testCalculateLuminanceLightShorthand() {
+ public function testCalculateLuminanceLightShorthand(): void {
$luminance = $this->util->calculateLuminance('#fff');
$this->assertEquals(1, $luminance);
}
- public function testCalculateLuminanceDarkShorthand() {
+ public function testCalculateLuminanceDarkShorthand(): void {
$luminance = $this->util->calculateLuminance('#000');
$this->assertEquals(0, $luminance);
}
- public function testInvertTextColorInvalid() {
- $invert = $this->util->invertTextColor('aaabbbcccddd123');
- $this->assertEquals(false, $invert);
+
+ public function testInvertTextColorInvalid(): void {
+ $this->expectException(\Exception::class);
+ $this->util->invertTextColor('aaabbbcccddd123');
}
- public function testInvertTextColorEmpty() {
- $invert = $this->util->invertTextColor('');
- $this->assertEquals(false, $invert);
+ public function testInvertTextColorEmpty(): void {
+ $this->expectException(\Exception::class);
+ $this->util->invertTextColor('');
}
- public function testElementColorDefault() {
- $elementColor = $this->util->elementColor("#000000");
+ public function testElementColorDefaultBlack(): void {
+ $elementColor = $this->util->elementColor('#000000');
+ $this->assertEquals('#4d4d4d', $elementColor);
+ }
+
+ public function testElementColorDefaultWhite(): void {
+ $elementColor = $this->util->elementColor('#ffffff');
+ $this->assertEquals('#b3b3b3', $elementColor);
+ }
+
+ public function testElementColorBlackOnDarkBackground(): void {
+ $elementColor = $this->util->elementColor('#000000', false);
+ $this->assertEquals('#4d4d4d', $elementColor);
+ }
+
+ public function testElementColorBlackOnBrightBackground(): void {
+ $elementColor = $this->util->elementColor('#000000', true);
$this->assertEquals('#000000', $elementColor);
}
- public function testElementColorOnDarkBackground() {
- $elementColor = $this->util->elementColor("#000000", false);
- $this->assertEquals('#555555', $elementColor);
+ public function testElementColorWhiteOnBrightBackground(): void {
+ $elementColor = $this->util->elementColor('#ffffff', true);
+ $this->assertEquals('#b3b3b3', $elementColor);
}
- public function testElementColorOnBrightBackground() {
- $elementColor = $this->util->elementColor('#ffffff');
- $this->assertEquals('#aaaaaa', $elementColor);
+ public function testElementColorWhiteOnDarkBackground(): void {
+ $elementColor = $this->util->elementColor('#ffffff', false);
+ $this->assertEquals('#ffffff', $elementColor);
}
- public function testGenerateRadioButtonWhite() {
+ public function testGenerateRadioButtonWhite(): void {
$button = $this->util->generateRadioButton('#ffffff');
$expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiNmZmZmZmYiLz48L3N2Zz4=';
$this->assertEquals($expected, $button);
}
- public function testGenerateRadioButtonBlack() {
+ public function testGenerateRadioButtonBlack(): void {
$button = $this->util->generateRadioButton('#000000');
$expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiMwMDAwMDAiLz48L3N2Zz4=';
$this->assertEquals($expected, $button);
}
- /**
- * @dataProvider dataGetAppIcon
- */
- public function testGetAppIcon($app, $expected) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetAppIcon')]
+ public function testGetAppIcon(string $app, string $expected): void {
$this->appData->expects($this->any())
->method('getFolder')
- ->with('images')
+ ->with('global/images')
->willThrowException(new NotFoundException());
- $this->appManager->expects($this->once())
- ->method('getAppPath')
- ->with($app)
- ->willReturn(\OC_App::getAppPath($app));
$icon = $this->util->getAppIcon($app);
$this->assertEquals($expected, $icon);
}
- public function dataGetAppIcon() {
+ public static function dataGetAppIcon(): array {
return [
- ['user_ldap', \OC_App::getAppPath('user_ldap') . '/img/app.svg'],
+ ['user_ldap', Server::get(IAppManager::class)->getAppPath('user_ldap') . '/img/app.svg'],
['noapplikethis', \OC::$SERVERROOT . '/core/img/logo/logo.svg'],
- ['comments', \OC_App::getAppPath('comments') . '/img/comments.svg'],
+ ['comments', Server::get(IAppManager::class)->getAppPath('comments') . '/img/comments.svg'],
];
}
- public function testGetAppIconThemed() {
+ public function testGetAppIconThemed(): void {
$file = $this->createMock(ISimpleFile::class);
$folder = $this->createMock(ISimpleFolder::class);
$folder->expects($this->once())
@@ -161,42 +166,34 @@ class UtilTest extends TestCase {
->willReturn($file);
$this->appData->expects($this->once())
->method('getFolder')
- ->with('images')
+ ->with('global/images')
->willReturn($folder);
$icon = $this->util->getAppIcon('noapplikethis');
$this->assertEquals($file, $icon);
}
- /**
- * @dataProvider dataGetAppImage
- */
- public function testGetAppImage($app, $image, $expected) {
- if ($app !== 'core') {
- $this->appManager->expects($this->once())
- ->method('getAppPath')
- ->with($app)
- ->willReturn(\OC_App::getAppPath($app));
- }
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetAppImage')]
+ public function testGetAppImage(string $app, string $image, string|bool $expected): void {
$this->assertEquals($expected, $this->util->getAppImage($app, $image));
}
- public function dataGetAppImage() {
+ public static function dataGetAppImage(): array {
return [
['core', 'logo/logo.svg', \OC::$SERVERROOT . '/core/img/logo/logo.svg'],
- ['files', 'external', \OC::$SERVERROOT . '/apps/files/img/external.svg'],
- ['files', 'external.svg', \OC::$SERVERROOT . '/apps/files/img/external.svg'],
+ ['files', 'folder', \OC::$SERVERROOT . '/apps/files/img/folder.svg'],
+ ['files', 'folder.svg', \OC::$SERVERROOT . '/apps/files/img/folder.svg'],
['noapplikethis', 'foobar.svg', false],
];
}
- public function testColorizeSvg() {
- $input = "#0082c9 #0082C9 #000000 #FFFFFF";
- $expected = "#AAAAAA #AAAAAA #000000 #FFFFFF";
+ public function testColorizeSvg(): void {
+ $input = '#0082c9 #0082C9 #000000 #FFFFFF';
+ $expected = '#AAAAAA #AAAAAA #000000 #FFFFFF';
$result = $this->util->colorizeSvg($input, '#AAAAAA');
$this->assertEquals($expected, $result);
}
- public function testIsAlreadyThemedFalse() {
+ public function testIsAlreadyThemedFalse(): void {
$this->config->expects($this->once())
->method('getSystemValue')
->with('theme', '')
@@ -205,7 +202,7 @@ class UtilTest extends TestCase {
$this->assertFalse($actual);
}
- public function testIsAlreadyThemedTrue() {
+ public function testIsAlreadyThemedTrue(): void {
$this->config->expects($this->once())
->method('getSystemValue')
->with('theme', '')
@@ -214,17 +211,15 @@ class UtilTest extends TestCase {
$this->assertTrue($actual);
}
- public function dataIsBackgroundThemed() {
+ public static function dataIsBackgroundThemed(): array {
return [
['', false],
['png', true],
['backgroundColor', false],
];
}
- /**
- * @dataProvider dataIsBackgroundThemed
- */
- public function testIsBackgroundThemed($backgroundMime, $expected) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataIsBackgroundThemed')]
+ public function testIsBackgroundThemed(string $backgroundMime, bool $expected): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('theming', 'backgroundMime', '')