From d41e98ab2dae51e329785d5f04d9fce3204f178f Mon Sep 17 00:00:00 2001 From: Arsalan Ul Haq Sohni Date: Tue, 2 Dec 2025 11:49:30 +0100 Subject: [PATCH 1/3] test(settings): add tests for developer documentation link generation Signed-off-by: Arsalan Ul Haq Sohni --- .../Controller/AppSettingsControllerTest.php | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/settings/tests/Controller/AppSettingsControllerTest.php b/apps/settings/tests/Controller/AppSettingsControllerTest.php index de96a10d31ee9..febfeffaa103e 100644 --- a/apps/settings/tests/Controller/AppSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AppSettingsControllerTest.php @@ -169,9 +169,21 @@ public function testViewApps(): void { ->method('setActiveEntry') ->with('core_apps'); + // Test that developer docs link is generated correctly + $this->urlGenerator + ->expects($this->once()) + ->method('linkToDocs') + ->with('developer-manual') + ->willReturn('https://docs.nextcloud.com/server/latest/developer_manual/'); + $this->initialState ->expects($this->exactly(4)) - ->method('provideInitialState'); + ->method('provideInitialState') + ->willReturnCallback(function ($key, $value) { + if ($key === 'appstoreDeveloperDocs') { + $this->assertEquals('https://docs.nextcloud.com/server/latest/developer_manual/', $value); + } + }); $policy = new ContentSecurityPolicy(); $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com'); @@ -202,9 +214,21 @@ public function testViewAppsAppstoreNotEnabled(): void { ->method('setActiveEntry') ->with('core_apps'); + // Test that developer docs link is still generated even when appstore is disabled + $this->urlGenerator + ->expects($this->once()) + ->method('linkToDocs') + ->with('developer-manual') + ->willReturn('https://docs.nextcloud.com/server/latest/developer_manual/'); + $this->initialState ->expects($this->exactly(4)) - ->method('provideInitialState'); + ->method('provideInitialState') + ->willReturnCallback(function ($key, $value) { + if ($key === 'appstoreDeveloperDocs') { + $this->assertEquals('https://docs.nextcloud.com/server/latest/developer_manual/', $value); + } + }); $policy = new ContentSecurityPolicy(); $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com'); From 1b06f8073425373e3998e27ebda6df772eaa9749 Mon Sep 17 00:00:00 2001 From: Arsalan Ul Haq Sohni Date: Tue, 2 Dec 2025 11:59:33 +0100 Subject: [PATCH 2/3] feat(settings): conditionally display developer documentation link based on configuration Signed-off-by: Arsalan Ul Haq Sohni --- .../lib/Controller/AppSettingsController.php | 9 +- .../settings/src/views/AppStoreNavigation.vue | 1 + .../Controller/AppSettingsControllerTest.php | 98 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/apps/settings/lib/Controller/AppSettingsController.php b/apps/settings/lib/Controller/AppSettingsController.php index bd713cab2013a..c8bea147d3ba3 100644 --- a/apps/settings/lib/Controller/AppSettingsController.php +++ b/apps/settings/lib/Controller/AppSettingsController.php @@ -36,6 +36,7 @@ use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\Http\Client\IClientService; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; @@ -76,6 +77,7 @@ public function __construct( private IInitialState $initialState, private AppDiscoverFetcher $discoverFetcher, private IClientService $clientService, + private IAppConfig $appConfig, ) { parent::__construct($appName, $request); $this->appData = $appDataFactory->get('appstore'); @@ -92,7 +94,12 @@ public function viewApps(): TemplateResponse { $this->initialState->provideInitialState('appstoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true)); $this->initialState->provideInitialState('appstoreBundles', $this->getBundles()); - $this->initialState->provideInitialState('appstoreDeveloperDocs', $this->urlGenerator->linkToDocs('developer-manual')); + + // Conditionally set developer docs link based on configuration + $displayDocumentationLink = $this->appConfig->getValueBool('settings', 'display_documentation_link', true); + $developerDocsUrl = $displayDocumentationLink ? $this->urlGenerator->linkToDocs('developer-manual') : ''; + $this->initialState->provideInitialState('appstoreDeveloperDocs', $developerDocsUrl); + $this->initialState->provideInitialState('appstoreUpdateCount', count($this->getAppsWithUpdates())); if ($this->appManager->isEnabledForAnyone('app_api')) { diff --git a/apps/settings/src/views/AppStoreNavigation.vue b/apps/settings/src/views/AppStoreNavigation.vue index b33636cdb7bf7..6b3bd06fa788e 100644 --- a/apps/settings/src/views/AppStoreNavigation.vue +++ b/apps/settings/src/views/AppStoreNavigation.vue @@ -101,6 +101,7 @@ diff --git a/apps/settings/tests/Controller/AppSettingsControllerTest.php b/apps/settings/tests/Controller/AppSettingsControllerTest.php index febfeffaa103e..723640051f82e 100644 --- a/apps/settings/tests/Controller/AppSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AppSettingsControllerTest.php @@ -20,6 +20,7 @@ use OCP\AppFramework\Services\IInitialState; use OCP\Files\AppData\IAppDataFactory; use OCP\Http\Client\IClientService; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; use OCP\INavigationManager; @@ -40,6 +41,7 @@ class AppSettingsControllerTest extends TestCase { private IRequest&MockObject $request; private IL10N&MockObject $l10n; private IConfig&MockObject $config; + private IAppConfig&MockObject $appConfig; private INavigationManager&MockObject $navigationManager; private AppManager&MockObject $appManager; private CategoryFetcher&MockObject $categoryFetcher; @@ -65,6 +67,7 @@ protected function setUp(): void { ->method('t') ->willReturnArgument(0); $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); $this->navigationManager = $this->createMock(INavigationManager::class); $this->appManager = $this->createMock(AppManager::class); $this->categoryFetcher = $this->createMock(CategoryFetcher::class); @@ -96,6 +99,7 @@ protected function setUp(): void { $this->initialState, $this->discoverFetcher, $this->clientService, + $this->appConfig, ); } @@ -164,6 +168,11 @@ public function testViewApps(): void { ->method('getSystemValueBool') ->with('appstoreenabled', true) ->willReturn(true); + $this->appConfig + ->expects($this->once()) + ->method('getValueBool') + ->with('settings', 'display_documentation_link', true) + ->willReturn(true); $this->navigationManager ->expects($this->once()) ->method('setActiveEntry') @@ -209,6 +218,11 @@ public function testViewAppsAppstoreNotEnabled(): void { ->method('getSystemValueBool') ->with('appstoreenabled', true) ->willReturn(false); + $this->appConfig + ->expects($this->once()) + ->method('getValueBool') + ->with('settings', 'display_documentation_link', true) + ->willReturn(true); $this->navigationManager ->expects($this->once()) ->method('setActiveEntry') @@ -243,4 +257,88 @@ public function testViewAppsAppstoreNotEnabled(): void { $this->assertEquals($expected, $this->appSettingsController->viewApps()); } + + public function testDeveloperDocumentationLinkHiddenWhenConfigured(): void { + $this->installer->expects($this->any()) + ->method('isUpdateAvailable') + ->willReturn(false); + $this->bundleFetcher->expects($this->once())->method('getBundles')->willReturn([]); + $this->config + ->expects($this->once()) + ->method('getSystemValueBool') + ->with('appstoreenabled', true) + ->willReturn(true); + $this->appConfig + ->expects($this->once()) + ->method('getValueBool') + ->with('settings', 'display_documentation_link', true) + ->willReturn(false); + $this->navigationManager + ->expects($this->once()) + ->method('setActiveEntry') + ->with('core_apps'); + + // When display_documentation_link is false, linkToDocs should not be called + $this->urlGenerator + ->expects($this->never()) + ->method('linkToDocs'); + + $providedStates = []; + $this->initialState + ->expects($this->exactly(4)) + ->method('provideInitialState') + ->willReturnCallback(function ($key, $value) use (&$providedStates) { + $providedStates[$key] = $value; + }); + + $this->appSettingsController->viewApps(); + + // Assert that the developer docs state was provided with an empty string + $this->assertArrayHasKey('appstoreDeveloperDocs', $providedStates); + $this->assertEquals('', $providedStates['appstoreDeveloperDocs']); + } + + public function testDeveloperDocumentationLinkShownByDefault(): void { + $this->installer->expects($this->any()) + ->method('isUpdateAvailable') + ->willReturn(false); + $this->bundleFetcher->expects($this->once())->method('getBundles')->willReturn([]); + $this->config + ->expects($this->once()) + ->method('getSystemValueBool') + ->with('appstoreenabled', true) + ->willReturn(true); + $this->appConfig + ->expects($this->once()) + ->method('getValueBool') + ->with('settings', 'display_documentation_link', true) + ->willReturn(true); + $this->navigationManager + ->expects($this->once()) + ->method('setActiveEntry') + ->with('core_apps'); + + $developerDocsUrl = 'https://docs.nextcloud.com/server/latest/developer_manual/'; + + // When display_documentation_link is true (default), linkToDocs should be called + $this->urlGenerator + ->expects($this->once()) + ->method('linkToDocs') + ->with('developer-manual') + ->willReturn($developerDocsUrl); + + $providedStates = []; + $this->initialState + ->expects($this->exactly(4)) + ->method('provideInitialState') + ->willReturnCallback(function ($key, $value) use (&$providedStates) { + $providedStates[$key] = $value; + }); + + $this->appSettingsController->viewApps(); + + // Assert that the developer docs state was provided with the correct URL + $this->assertArrayHasKey('appstoreDeveloperDocs', $providedStates); + $this->assertEquals($developerDocsUrl, $providedStates['appstoreDeveloperDocs']); + } } From 46858efa3ba0b84711d11fd68d8baf0dbdbd5036 Mon Sep 17 00:00:00 2001 From: Arsalan Ul Haq Sohni Date: Tue, 30 Dec 2025 12:42:43 +0100 Subject: [PATCH 3/3] docs: remove developer documentation link Signed-off-by: Arsalan Ul Haq Sohni --- .../lib/Controller/AppSettingsController.php | 8 -- .../settings/src/views/AppStoreNavigation.vue | 6 - .../Controller/AppSettingsControllerTest.php | 126 +----------------- 3 files changed, 2 insertions(+), 138 deletions(-) diff --git a/apps/settings/lib/Controller/AppSettingsController.php b/apps/settings/lib/Controller/AppSettingsController.php index c8bea147d3ba3..b0a1b5a49dbf7 100644 --- a/apps/settings/lib/Controller/AppSettingsController.php +++ b/apps/settings/lib/Controller/AppSettingsController.php @@ -36,7 +36,6 @@ use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\Http\Client\IClientService; -use OCP\IAppConfig; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; @@ -77,7 +76,6 @@ public function __construct( private IInitialState $initialState, private AppDiscoverFetcher $discoverFetcher, private IClientService $clientService, - private IAppConfig $appConfig, ) { parent::__construct($appName, $request); $this->appData = $appDataFactory->get('appstore'); @@ -94,12 +92,6 @@ public function viewApps(): TemplateResponse { $this->initialState->provideInitialState('appstoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true)); $this->initialState->provideInitialState('appstoreBundles', $this->getBundles()); - - // Conditionally set developer docs link based on configuration - $displayDocumentationLink = $this->appConfig->getValueBool('settings', 'display_documentation_link', true); - $developerDocsUrl = $displayDocumentationLink ? $this->urlGenerator->linkToDocs('developer-manual') : ''; - $this->initialState->provideInitialState('appstoreDeveloperDocs', $developerDocsUrl); - $this->initialState->provideInitialState('appstoreUpdateCount', count($this->getAppsWithUpdates())); if ($this->appManager->isEnabledForAnyone('app_api')) { diff --git a/apps/settings/src/views/AppStoreNavigation.vue b/apps/settings/src/views/AppStoreNavigation.vue index 6b3bd06fa788e..3588c2c17ad1f 100644 --- a/apps/settings/src/views/AppStoreNavigation.vue +++ b/apps/settings/src/views/AppStoreNavigation.vue @@ -100,11 +100,6 @@ - @@ -124,7 +119,6 @@ import APPSTORE_CATEGORY_ICONS from '../constants/AppstoreCategoryIcons.ts' import { useAppsStore } from '../store/apps-store.ts' const appstoreEnabled = loadState('settings', 'appstoreEnabled', true) -const developerDocsUrl = loadState('settings', 'appstoreDeveloperDocs', '') const store = useAppsStore() const categories = computed(() => store.categories) diff --git a/apps/settings/tests/Controller/AppSettingsControllerTest.php b/apps/settings/tests/Controller/AppSettingsControllerTest.php index 723640051f82e..de96a10d31ee9 100644 --- a/apps/settings/tests/Controller/AppSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AppSettingsControllerTest.php @@ -20,7 +20,6 @@ use OCP\AppFramework\Services\IInitialState; use OCP\Files\AppData\IAppDataFactory; use OCP\Http\Client\IClientService; -use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; use OCP\INavigationManager; @@ -41,7 +40,6 @@ class AppSettingsControllerTest extends TestCase { private IRequest&MockObject $request; private IL10N&MockObject $l10n; private IConfig&MockObject $config; - private IAppConfig&MockObject $appConfig; private INavigationManager&MockObject $navigationManager; private AppManager&MockObject $appManager; private CategoryFetcher&MockObject $categoryFetcher; @@ -67,7 +65,6 @@ protected function setUp(): void { ->method('t') ->willReturnArgument(0); $this->config = $this->createMock(IConfig::class); - $this->appConfig = $this->createMock(IAppConfig::class); $this->navigationManager = $this->createMock(INavigationManager::class); $this->appManager = $this->createMock(AppManager::class); $this->categoryFetcher = $this->createMock(CategoryFetcher::class); @@ -99,7 +96,6 @@ protected function setUp(): void { $this->initialState, $this->discoverFetcher, $this->clientService, - $this->appConfig, ); } @@ -168,31 +164,14 @@ public function testViewApps(): void { ->method('getSystemValueBool') ->with('appstoreenabled', true) ->willReturn(true); - $this->appConfig - ->expects($this->once()) - ->method('getValueBool') - ->with('settings', 'display_documentation_link', true) - ->willReturn(true); $this->navigationManager ->expects($this->once()) ->method('setActiveEntry') ->with('core_apps'); - // Test that developer docs link is generated correctly - $this->urlGenerator - ->expects($this->once()) - ->method('linkToDocs') - ->with('developer-manual') - ->willReturn('https://docs.nextcloud.com/server/latest/developer_manual/'); - $this->initialState ->expects($this->exactly(4)) - ->method('provideInitialState') - ->willReturnCallback(function ($key, $value) { - if ($key === 'appstoreDeveloperDocs') { - $this->assertEquals('https://docs.nextcloud.com/server/latest/developer_manual/', $value); - } - }); + ->method('provideInitialState'); $policy = new ContentSecurityPolicy(); $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com'); @@ -218,31 +197,14 @@ public function testViewAppsAppstoreNotEnabled(): void { ->method('getSystemValueBool') ->with('appstoreenabled', true) ->willReturn(false); - $this->appConfig - ->expects($this->once()) - ->method('getValueBool') - ->with('settings', 'display_documentation_link', true) - ->willReturn(true); $this->navigationManager ->expects($this->once()) ->method('setActiveEntry') ->with('core_apps'); - // Test that developer docs link is still generated even when appstore is disabled - $this->urlGenerator - ->expects($this->once()) - ->method('linkToDocs') - ->with('developer-manual') - ->willReturn('https://docs.nextcloud.com/server/latest/developer_manual/'); - $this->initialState ->expects($this->exactly(4)) - ->method('provideInitialState') - ->willReturnCallback(function ($key, $value) { - if ($key === 'appstoreDeveloperDocs') { - $this->assertEquals('https://docs.nextcloud.com/server/latest/developer_manual/', $value); - } - }); + ->method('provideInitialState'); $policy = new ContentSecurityPolicy(); $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com'); @@ -257,88 +219,4 @@ public function testViewAppsAppstoreNotEnabled(): void { $this->assertEquals($expected, $this->appSettingsController->viewApps()); } - - public function testDeveloperDocumentationLinkHiddenWhenConfigured(): void { - $this->installer->expects($this->any()) - ->method('isUpdateAvailable') - ->willReturn(false); - $this->bundleFetcher->expects($this->once())->method('getBundles')->willReturn([]); - $this->config - ->expects($this->once()) - ->method('getSystemValueBool') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->appConfig - ->expects($this->once()) - ->method('getValueBool') - ->with('settings', 'display_documentation_link', true) - ->willReturn(false); - $this->navigationManager - ->expects($this->once()) - ->method('setActiveEntry') - ->with('core_apps'); - - // When display_documentation_link is false, linkToDocs should not be called - $this->urlGenerator - ->expects($this->never()) - ->method('linkToDocs'); - - $providedStates = []; - $this->initialState - ->expects($this->exactly(4)) - ->method('provideInitialState') - ->willReturnCallback(function ($key, $value) use (&$providedStates) { - $providedStates[$key] = $value; - }); - - $this->appSettingsController->viewApps(); - - // Assert that the developer docs state was provided with an empty string - $this->assertArrayHasKey('appstoreDeveloperDocs', $providedStates); - $this->assertEquals('', $providedStates['appstoreDeveloperDocs']); - } - - public function testDeveloperDocumentationLinkShownByDefault(): void { - $this->installer->expects($this->any()) - ->method('isUpdateAvailable') - ->willReturn(false); - $this->bundleFetcher->expects($this->once())->method('getBundles')->willReturn([]); - $this->config - ->expects($this->once()) - ->method('getSystemValueBool') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->appConfig - ->expects($this->once()) - ->method('getValueBool') - ->with('settings', 'display_documentation_link', true) - ->willReturn(true); - $this->navigationManager - ->expects($this->once()) - ->method('setActiveEntry') - ->with('core_apps'); - - $developerDocsUrl = 'https://docs.nextcloud.com/server/latest/developer_manual/'; - - // When display_documentation_link is true (default), linkToDocs should be called - $this->urlGenerator - ->expects($this->once()) - ->method('linkToDocs') - ->with('developer-manual') - ->willReturn($developerDocsUrl); - - $providedStates = []; - $this->initialState - ->expects($this->exactly(4)) - ->method('provideInitialState') - ->willReturnCallback(function ($key, $value) use (&$providedStates) { - $providedStates[$key] = $value; - }); - - $this->appSettingsController->viewApps(); - - // Assert that the developer docs state was provided with the correct URL - $this->assertArrayHasKey('appstoreDeveloperDocs', $providedStates); - $this->assertEquals($developerDocsUrl, $providedStates['appstoreDeveloperDocs']); - } }