You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ThemingControllerTest.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
  4. *
  5. * @author Bjoern Schiessle <bjoern@schiessle.org>
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author John Molakvoæ <skjnldsv@protonmail.com>
  10. * @author Julius Haertl <jus@bitgrid.net>
  11. * @author Julius Härtl <jus@bitgrid.net>
  12. * @author Kyle Fazzari <kyrofa@ubuntu.com>
  13. * @author Lukas Reschke <lukas@statuscode.ch>
  14. * @author Michael Weimann <mail@michael-weimann.eu>
  15. * @author rakekniven <mark.ziegler@rakekniven.de>
  16. * @author Roeland Jago Douma <roeland@famdouma.nl>
  17. *
  18. * @license GNU AGPL version 3 or any later version
  19. *
  20. * This program is free software: you can redistribute it and/or modify
  21. * it under the terms of the GNU Affero General Public License as
  22. * published by the Free Software Foundation, either version 3 of the
  23. * License, or (at your option) any later version.
  24. *
  25. * This program is distributed in the hope that it will be useful,
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. * GNU Affero General Public License for more details.
  29. *
  30. * You should have received a copy of the GNU Affero General Public License
  31. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  32. *
  33. */
  34. namespace OCA\Theming\Tests\Controller;
  35. use OC\L10N\L10N;
  36. use OC\Template\SCSSCacher;
  37. use OCA\Theming\Controller\ThemingController;
  38. use OCA\Theming\ImageManager;
  39. use OCA\Theming\ThemingDefaults;
  40. use OCP\App\IAppManager;
  41. use OCP\AppFramework\Http;
  42. use OCP\AppFramework\Http\DataResponse;
  43. use OCP\AppFramework\Utility\ITimeFactory;
  44. use OCP\Files\IAppData;
  45. use OCP\Files\NotFoundException;
  46. use OCP\Files\SimpleFS\ISimpleFile;
  47. use OCP\IConfig;
  48. use OCP\IL10N;
  49. use OCP\IRequest;
  50. use OCP\ITempManager;
  51. use OCP\IURLGenerator;
  52. use PHPUnit\Framework\MockObject\MockObject;
  53. use Test\TestCase;
  54. class ThemingControllerTest extends TestCase {
  55. /** @var IRequest|MockObject */
  56. private $request;
  57. /** @var IConfig|MockObject */
  58. private $config;
  59. /** @var ThemingDefaults|MockObject */
  60. private $themingDefaults;
  61. /** @var IL10N|MockObject */
  62. private $l10n;
  63. /** @var ThemingController */
  64. private $themingController;
  65. /** @var ITempManager */
  66. private $tempManager;
  67. /** @var IAppManager|MockObject */
  68. private $appManager;
  69. /** @var IAppData|MockObject */
  70. private $appData;
  71. /** @var ImageManager|MockObject */
  72. private $imageManager;
  73. /** @var SCSSCacher */
  74. private $scssCacher;
  75. /** @var IURLGenerator */
  76. private $urlGenerator;
  77. protected function setUp(): void {
  78. $this->request = $this->createMock(IRequest::class);
  79. $this->config = $this->createMock(IConfig::class);
  80. $this->themingDefaults = $this->createMock(ThemingDefaults::class);
  81. $this->l10n = $this->createMock(L10N::class);
  82. $this->appData = $this->createMock(IAppData::class);
  83. $this->appManager = $this->createMock(IAppManager::class);
  84. $this->tempManager = \OC::$server->getTempManager();
  85. $this->scssCacher = $this->createMock(SCSSCacher::class);
  86. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  87. $this->imageManager = $this->createMock(ImageManager::class);
  88. $timeFactory = $this->createMock(ITimeFactory::class);
  89. $timeFactory->expects($this->any())
  90. ->method('getTime')
  91. ->willReturn(123);
  92. $this->overwriteService(ITimeFactory::class, $timeFactory);
  93. $this->themingController = new ThemingController(
  94. 'theming',
  95. $this->request,
  96. $this->config,
  97. $this->themingDefaults,
  98. $this->l10n,
  99. $this->tempManager,
  100. $this->appData,
  101. $this->scssCacher,
  102. $this->urlGenerator,
  103. $this->appManager,
  104. $this->imageManager
  105. );
  106. parent::setUp();
  107. }
  108. public function dataUpdateStylesheetSuccess() {
  109. return [
  110. ['name', str_repeat('a', 250), 'Saved'],
  111. ['url', 'https://nextcloud.com/' . str_repeat('a', 478), 'Saved'],
  112. ['slogan', str_repeat('a', 500), 'Saved'],
  113. ['color', '#0082c9', 'Saved'],
  114. ['color', '#0082C9', 'Saved'],
  115. ['color', '#0082C9', 'Saved'],
  116. ['imprintUrl', 'https://nextcloud.com/' . str_repeat('a', 478), 'Saved'],
  117. ['privacyUrl', 'https://nextcloud.com/' . str_repeat('a', 478), 'Saved'],
  118. ];
  119. }
  120. /**
  121. * @dataProvider dataUpdateStylesheetSuccess
  122. *
  123. * @param string $setting
  124. * @param string $value
  125. * @param string $message
  126. */
  127. public function testUpdateStylesheetSuccess($setting, $value, $message) {
  128. $this->themingDefaults
  129. ->expects($this->once())
  130. ->method('set')
  131. ->with($setting, $value);
  132. $this->l10n
  133. ->expects($this->once())
  134. ->method('t')
  135. ->willReturnCallback(function ($str) {
  136. return $str;
  137. });
  138. $this->scssCacher
  139. ->expects($this->once())
  140. ->method('getCachedSCSS')
  141. ->with('core', '/core/css/css-variables.scss')
  142. ->willReturn('/core/css/someHash-css-variables.scss');
  143. $this->urlGenerator
  144. ->expects($this->once())
  145. ->method('linkTo')
  146. ->with('', '/core/css/someHash-css-variables.scss')
  147. ->willReturn('/nextcloudWebroot/core/css/someHash-css-variables.scss');
  148. $expected = new DataResponse(
  149. [
  150. 'data' =>
  151. [
  152. 'message' => $message,
  153. 'serverCssUrl' => '/nextcloudWebroot/core/css/someHash-css-variables.scss',
  154. ],
  155. 'status' => 'success',
  156. ]
  157. );
  158. $this->assertEquals($expected, $this->themingController->updateStylesheet($setting, $value));
  159. }
  160. public function dataUpdateStylesheetError() {
  161. return [
  162. ['name', str_repeat('a', 251), 'The given name is too long'],
  163. ['url', 'http://example.com/' . str_repeat('a', 501), 'The given web address is too long'],
  164. ['url', str_repeat('a', 501), 'The given web address is not a valid URL'],
  165. ['url', 'javascript:alert(1)', 'The given web address is not a valid URL'],
  166. ['slogan', str_repeat('a', 501), 'The given slogan is too long'],
  167. ['color', '0082C9', 'The given color is invalid'],
  168. ['color', '#0082Z9', 'The given color is invalid'],
  169. ['color', 'Nextcloud', 'The given color is invalid'],
  170. ['imprintUrl', '0082C9', 'The given legal notice address is not a valid URL'],
  171. ['imprintUrl', '0082C9', 'The given legal notice address is not a valid URL'],
  172. ['imprintUrl', 'javascript:foo', 'The given legal notice address is not a valid URL'],
  173. ['privacyUrl', '#0082Z9', 'The given privacy policy address is not a valid URL'],
  174. ];
  175. }
  176. /**
  177. * @dataProvider dataUpdateStylesheetError
  178. *
  179. * @param string $setting
  180. * @param string $value
  181. * @param string $message
  182. */
  183. public function testUpdateStylesheetError($setting, $value, $message) {
  184. $this->themingDefaults
  185. ->expects($this->never())
  186. ->method('set')
  187. ->with($setting, $value);
  188. $this->l10n
  189. ->expects($this->any())
  190. ->method('t')
  191. ->willReturnCallback(function ($str) {
  192. return $str;
  193. });
  194. $expected = new DataResponse(
  195. [
  196. 'data' =>
  197. [
  198. 'message' => $message,
  199. ],
  200. 'status' => 'error',
  201. ],
  202. Http::STATUS_BAD_REQUEST
  203. );
  204. $this->assertEquals($expected, $this->themingController->updateStylesheet($setting, $value));
  205. }
  206. public function testUpdateLogoNoData() {
  207. $this->request
  208. ->expects($this->at(0))
  209. ->method('getParam')
  210. ->with('key')
  211. ->willReturn('logo');
  212. $this->request
  213. ->expects($this->at(1))
  214. ->method('getUploadedFile')
  215. ->with('image')
  216. ->willReturn(null);
  217. $this->l10n
  218. ->expects($this->any())
  219. ->method('t')
  220. ->willReturnCallback(function ($str) {
  221. return $str;
  222. });
  223. $expected = new DataResponse(
  224. [
  225. 'data' =>
  226. [
  227. 'message' => 'No file uploaded',
  228. ],
  229. 'status' => 'failure',
  230. ],
  231. Http::STATUS_UNPROCESSABLE_ENTITY
  232. );
  233. $this->assertEquals($expected, $this->themingController->uploadImage());
  234. }
  235. /**
  236. * Checks that trying to upload an SVG favicon without imagemagick
  237. * results in an unsupported media type response.
  238. *
  239. * @test
  240. * @return void
  241. */
  242. public function testUploadSVGFaviconWithoutImagemagick() {
  243. $this->imageManager
  244. ->method('shouldReplaceIcons')
  245. ->willReturn(false);
  246. $this->request
  247. ->expects($this->at(0))
  248. ->method('getParam')
  249. ->with('key')
  250. ->willReturn('favicon');
  251. $this->request
  252. ->expects($this->at(1))
  253. ->method('getUploadedFile')
  254. ->with('image')
  255. ->willReturn([
  256. 'tmp_name' => __DIR__ . '/../../../../tests/data/testimagelarge.svg',
  257. 'type' => 'image/svg',
  258. 'name' => 'testimagelarge.svg',
  259. 'error' => 0,
  260. ]);
  261. $this->l10n
  262. ->expects($this->any())
  263. ->method('t')
  264. ->willReturnCallback(function ($str) {
  265. return $str;
  266. });
  267. $this->imageManager->expects($this->once())
  268. ->method('updateImage')
  269. ->willThrowException(new \Exception('Unsupported image type'));
  270. $expected = new DataResponse(
  271. [
  272. 'data' =>
  273. [
  274. 'message' => 'Unsupported image type',
  275. ],
  276. 'status' => 'failure'
  277. ],
  278. Http::STATUS_UNPROCESSABLE_ENTITY
  279. );
  280. $this->assertEquals($expected, $this->themingController->uploadImage());
  281. }
  282. public function testUpdateLogoInvalidMimeType() {
  283. $this->request
  284. ->expects($this->at(0))
  285. ->method('getParam')
  286. ->with('key')
  287. ->willReturn('logo');
  288. $this->request
  289. ->expects($this->at(1))
  290. ->method('getUploadedFile')
  291. ->with('image')
  292. ->willReturn([
  293. 'tmp_name' => __DIR__ . '/../../../../tests/data/lorem.txt',
  294. 'type' => 'application/pdf',
  295. 'name' => 'logo.pdf',
  296. 'error' => 0,
  297. ]);
  298. $this->l10n
  299. ->expects($this->any())
  300. ->method('t')
  301. ->willReturnCallback(function ($str) {
  302. return $str;
  303. });
  304. $this->imageManager->expects($this->once())
  305. ->method('updateImage')
  306. ->willThrowException(new \Exception('Unsupported image type'));
  307. $expected = new DataResponse(
  308. [
  309. 'data' =>
  310. [
  311. 'message' => 'Unsupported image type',
  312. ],
  313. 'status' => 'failure'
  314. ],
  315. Http::STATUS_UNPROCESSABLE_ENTITY
  316. );
  317. $this->assertEquals($expected, $this->themingController->uploadImage());
  318. }
  319. public function dataUpdateImages() {
  320. return [
  321. ['image/jpeg', false],
  322. ['image/jpeg', true],
  323. ['image/gif'],
  324. ['image/png'],
  325. ['image/svg+xml'],
  326. ['image/svg']
  327. ];
  328. }
  329. /** @dataProvider dataUpdateImages */
  330. public function testUpdateLogoNormalLogoUpload($mimeType, $folderExists = true) {
  331. $tmpLogo = \OC::$server->getTempManager()->getTemporaryFolder() . '/logo.svg';
  332. $destination = \OC::$server->getTempManager()->getTemporaryFolder();
  333. touch($tmpLogo);
  334. copy(__DIR__ . '/../../../../tests/data/testimage.png', $tmpLogo);
  335. $this->request
  336. ->expects($this->at(0))
  337. ->method('getParam')
  338. ->with('key')
  339. ->willReturn('logo');
  340. $this->request
  341. ->expects($this->at(1))
  342. ->method('getUploadedFile')
  343. ->with('image')
  344. ->willReturn([
  345. 'tmp_name' => $tmpLogo,
  346. 'type' => $mimeType,
  347. 'name' => 'logo.svg',
  348. 'error' => 0,
  349. ]);
  350. $this->l10n
  351. ->expects($this->any())
  352. ->method('t')
  353. ->willReturnCallback(function ($str) {
  354. return $str;
  355. });
  356. $this->urlGenerator->expects($this->once())
  357. ->method('linkTo')
  358. ->willReturn('serverCss');
  359. $this->imageManager->expects($this->once())
  360. ->method('getImageUrl')
  361. ->with('logo')
  362. ->willReturn('imageUrl');
  363. $this->imageManager->expects($this->once())
  364. ->method('updateImage');
  365. $expected = new DataResponse(
  366. [
  367. 'data' =>
  368. [
  369. 'name' => 'logo.svg',
  370. 'message' => 'Saved',
  371. 'url' => 'imageUrl',
  372. 'serverCssUrl' => 'serverCss'
  373. ],
  374. 'status' => 'success'
  375. ]
  376. );
  377. $this->assertEquals($expected, $this->themingController->uploadImage());
  378. }
  379. /** @dataProvider dataUpdateImages */
  380. public function testUpdateLogoLoginScreenUpload($folderExists) {
  381. $tmpLogo = \OC::$server->getTempManager()->getTemporaryFolder() . 'logo.png';
  382. touch($tmpLogo);
  383. copy(__DIR__ . '/../../../../tests/data/desktopapp.png', $tmpLogo);
  384. $this->request
  385. ->expects($this->at(0))
  386. ->method('getParam')
  387. ->with('key')
  388. ->willReturn('background');
  389. $this->request
  390. ->expects($this->at(1))
  391. ->method('getUploadedFile')
  392. ->with('image')
  393. ->willReturn([
  394. 'tmp_name' => $tmpLogo,
  395. 'type' => 'image/svg+xml',
  396. 'name' => 'logo.svg',
  397. 'error' => 0,
  398. ]);
  399. $this->l10n
  400. ->expects($this->any())
  401. ->method('t')
  402. ->willReturnCallback(function ($str) {
  403. return $str;
  404. });
  405. $this->imageManager->expects($this->once())
  406. ->method('updateImage');
  407. $this->urlGenerator->expects($this->once())
  408. ->method('linkTo')
  409. ->willReturn('serverCss');
  410. $this->imageManager->expects($this->once())
  411. ->method('getImageUrl')
  412. ->with('background')
  413. ->willReturn('imageUrl');
  414. $expected = new DataResponse(
  415. [
  416. 'data' =>
  417. [
  418. 'name' => 'logo.svg',
  419. 'message' => 'Saved',
  420. 'url' => 'imageUrl',
  421. 'serverCssUrl' => 'serverCss'
  422. ],
  423. 'status' => 'success'
  424. ]
  425. );
  426. $this->assertEquals($expected, $this->themingController->uploadImage());
  427. }
  428. public function testUpdateLogoLoginScreenUploadWithInvalidImage() {
  429. $tmpLogo = \OC::$server->getTempManager()->getTemporaryFolder() . '/logo.svg';
  430. touch($tmpLogo);
  431. file_put_contents($tmpLogo, file_get_contents(__DIR__ . '/../../../../tests/data/data.zip'));
  432. $this->request
  433. ->expects($this->at(0))
  434. ->method('getParam')
  435. ->with('key')
  436. ->willReturn('logo');
  437. $this->request
  438. ->expects($this->at(1))
  439. ->method('getUploadedFile')
  440. ->with('image')
  441. ->willReturn([
  442. 'tmp_name' => $tmpLogo,
  443. 'type' => 'foobar',
  444. 'name' => 'logo.svg',
  445. 'error' => 0,
  446. ]);
  447. $this->l10n
  448. ->expects($this->any())
  449. ->method('t')
  450. ->willReturnCallback(function ($str) {
  451. return $str;
  452. });
  453. $this->imageManager->expects($this->once())
  454. ->method('updateImage')
  455. ->willThrowException(new \Exception('Unsupported image type'));
  456. $expected = new DataResponse(
  457. [
  458. 'data' =>
  459. [
  460. 'message' => 'Unsupported image type',
  461. ],
  462. 'status' => 'failure'
  463. ],
  464. Http::STATUS_UNPROCESSABLE_ENTITY
  465. );
  466. $this->assertEquals($expected, $this->themingController->uploadImage());
  467. }
  468. public function dataPhpUploadErrors() {
  469. return [
  470. [UPLOAD_ERR_INI_SIZE, 'The uploaded file exceeds the upload_max_filesize directive in php.ini'],
  471. [UPLOAD_ERR_FORM_SIZE, 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'],
  472. [UPLOAD_ERR_PARTIAL, 'The file was only partially uploaded'],
  473. [UPLOAD_ERR_NO_FILE, 'No file was uploaded'],
  474. [UPLOAD_ERR_NO_TMP_DIR, 'Missing a temporary folder'],
  475. [UPLOAD_ERR_CANT_WRITE, 'Could not write file to disk'],
  476. [UPLOAD_ERR_EXTENSION, 'A PHP extension stopped the file upload'],
  477. ];
  478. }
  479. /**
  480. * @dataProvider dataPhpUploadErrors
  481. */
  482. public function testUpdateLogoLoginScreenUploadWithInvalidImageUpload($error, $expectedErrorMessage) {
  483. $this->request
  484. ->expects($this->at(0))
  485. ->method('getParam')
  486. ->with('key')
  487. ->willReturn('background');
  488. $this->request
  489. ->expects($this->at(1))
  490. ->method('getUploadedFile')
  491. ->with('image')
  492. ->willReturn([
  493. 'tmp_name' => '',
  494. 'type' => 'image/svg+xml',
  495. 'name' => 'logo.svg',
  496. 'error' => $error,
  497. ]);
  498. $this->l10n
  499. ->expects($this->any())
  500. ->method('t')
  501. ->willReturnCallback(function ($str) {
  502. return $str;
  503. });
  504. $expected = new DataResponse(
  505. [
  506. 'data' =>
  507. [
  508. 'message' => $expectedErrorMessage,
  509. ],
  510. 'status' => 'failure'
  511. ],
  512. Http::STATUS_UNPROCESSABLE_ENTITY
  513. );
  514. $this->assertEquals($expected, $this->themingController->uploadImage());
  515. }
  516. /**
  517. * @dataProvider dataPhpUploadErrors
  518. */
  519. public function testUpdateLogoUploadWithInvalidImageUpload($error, $expectedErrorMessage) {
  520. $this->request
  521. ->expects($this->at(0))
  522. ->method('getParam')
  523. ->with('key')
  524. ->willReturn('background');
  525. $this->request
  526. ->expects($this->at(1))
  527. ->method('getUploadedFile')
  528. ->with('image')
  529. ->willReturn([
  530. 'tmp_name' => '',
  531. 'type' => 'text/svg',
  532. 'name' => 'logo.svg',
  533. 'error' => $error,
  534. ]);
  535. $this->l10n
  536. ->expects($this->any())
  537. ->method('t')
  538. ->willReturnCallback(function ($str) {
  539. return $str;
  540. });
  541. $expected = new DataResponse(
  542. [
  543. 'data' =>
  544. [
  545. 'message' => $expectedErrorMessage
  546. ],
  547. 'status' => 'failure'
  548. ],
  549. Http::STATUS_UNPROCESSABLE_ENTITY
  550. );
  551. $this->assertEquals($expected, $this->themingController->uploadImage());
  552. }
  553. public function testUndo() {
  554. $this->l10n
  555. ->expects($this->once())
  556. ->method('t')
  557. ->with('Saved')
  558. ->willReturn('Saved');
  559. $this->themingDefaults
  560. ->expects($this->once())
  561. ->method('undo')
  562. ->with('MySetting')
  563. ->willReturn('MyValue');
  564. $this->scssCacher
  565. ->expects($this->once())
  566. ->method('getCachedSCSS')
  567. ->with('core', '/core/css/css-variables.scss')
  568. ->willReturn('/core/css/someHash-css-variables.scss');
  569. $this->urlGenerator
  570. ->expects($this->once())
  571. ->method('linkTo')
  572. ->with('', '/core/css/someHash-css-variables.scss')
  573. ->willReturn('/nextcloudWebroot/core/css/someHash-css-variables.scss');
  574. $expected = new DataResponse(
  575. [
  576. 'data' =>
  577. [
  578. 'value' => 'MyValue',
  579. 'message' => 'Saved',
  580. 'serverCssUrl' => '/nextcloudWebroot/core/css/someHash-css-variables.scss',
  581. ],
  582. 'status' => 'success'
  583. ]
  584. );
  585. $this->assertEquals($expected, $this->themingController->undo('MySetting'));
  586. }
  587. public function dataUndoDelete() {
  588. return [
  589. [ 'backgroundMime', 'background' ],
  590. [ 'logoMime', 'logo' ]
  591. ];
  592. }
  593. /** @dataProvider dataUndoDelete */
  594. public function testUndoDelete($value, $filename) {
  595. $this->l10n
  596. ->expects($this->once())
  597. ->method('t')
  598. ->with('Saved')
  599. ->willReturn('Saved');
  600. $this->themingDefaults
  601. ->expects($this->once())
  602. ->method('undo')
  603. ->with($value)
  604. ->willReturn($value);
  605. $this->scssCacher
  606. ->expects($this->once())
  607. ->method('getCachedSCSS')
  608. ->with('core', '/core/css/css-variables.scss')
  609. ->willReturn('/core/css/someHash-css-variables.scss');
  610. $this->urlGenerator
  611. ->expects($this->once())
  612. ->method('linkTo')
  613. ->with('', '/core/css/someHash-css-variables.scss')
  614. ->willReturn('/nextcloudWebroot/core/css/someHash-css-variables.scss');
  615. $expected = new DataResponse(
  616. [
  617. 'data' =>
  618. [
  619. 'value' => $value,
  620. 'message' => 'Saved',
  621. 'serverCssUrl' => '/nextcloudWebroot/core/css/someHash-css-variables.scss',
  622. ],
  623. 'status' => 'success'
  624. ]
  625. );
  626. $this->assertEquals($expected, $this->themingController->undo($value));
  627. }
  628. public function testGetLogoNotExistent() {
  629. $this->imageManager->method('getImage')
  630. ->with($this->equalTo('logo'))
  631. ->willThrowException(new NotFoundException());
  632. $expected = new Http\NotFoundResponse();
  633. $this->assertEquals($expected, $this->themingController->getImage('logo'));
  634. }
  635. public function testGetLogo() {
  636. $file = $this->createMock(ISimpleFile::class);
  637. $file->method('getName')->willReturn('logo.svg');
  638. $file->method('getMTime')->willReturn(42);
  639. $this->imageManager->expects($this->once())
  640. ->method('getImage')
  641. ->willReturn($file);
  642. $this->config
  643. ->expects($this->any())
  644. ->method('getAppValue')
  645. ->with('theming', 'logoMime', '')
  646. ->willReturn('text/svg');
  647. @$expected = new Http\FileDisplayResponse($file);
  648. $expected->cacheFor(3600);
  649. $expected->addHeader('Content-Type', 'text/svg');
  650. $expected->addHeader('Content-Disposition', 'attachment; filename="logo"');
  651. $csp = new Http\ContentSecurityPolicy();
  652. $csp->allowInlineStyle();
  653. $expected->setContentSecurityPolicy($csp);
  654. @$this->assertEquals($expected, $this->themingController->getImage('logo'));
  655. }
  656. public function testGetLoginBackgroundNotExistent() {
  657. $this->imageManager->method('getImage')
  658. ->with($this->equalTo('background'))
  659. ->willThrowException(new NotFoundException());
  660. $expected = new Http\NotFoundResponse();
  661. $this->assertEquals($expected, $this->themingController->getImage('background'));
  662. }
  663. public function testGetLoginBackground() {
  664. $file = $this->createMock(ISimpleFile::class);
  665. $file->method('getName')->willReturn('background.png');
  666. $file->method('getMTime')->willReturn(42);
  667. $this->imageManager->expects($this->once())
  668. ->method('getImage')
  669. ->willReturn($file);
  670. $this->config
  671. ->expects($this->any())
  672. ->method('getAppValue')
  673. ->with('theming', 'backgroundMime', '')
  674. ->willReturn('image/png');
  675. @$expected = new Http\FileDisplayResponse($file);
  676. $expected->cacheFor(3600);
  677. $expected->addHeader('Content-Type', 'image/png');
  678. $expected->addHeader('Content-Disposition', 'attachment; filename="background"');
  679. $csp = new Http\ContentSecurityPolicy();
  680. $csp->allowInlineStyle();
  681. $expected->setContentSecurityPolicy($csp);
  682. @$this->assertEquals($expected, $this->themingController->getImage('background'));
  683. }
  684. public function testGetStylesheet() {
  685. $this->appManager->expects($this->once())->method('getAppPath')->with('theming')->willReturn(\OC::$SERVERROOT . '/theming');
  686. $file = $this->createMock(ISimpleFile::class);
  687. $file->expects($this->any())->method('getName')->willReturn('theming.css');
  688. $file->expects($this->any())->method('getMTime')->willReturn(42);
  689. $file->expects($this->any())->method('getContent')->willReturn('compiled');
  690. $this->scssCacher->expects($this->once())->method('process')->willReturn(true);
  691. $this->scssCacher->expects($this->once())->method('getCachedCSS')->willReturn($file);
  692. $response = new Http\FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']);
  693. $response->cacheFor(86400);
  694. $actual = $this->themingController->getStylesheet();
  695. $this->assertEquals($response, $actual);
  696. }
  697. public function testGetStylesheetFails() {
  698. $this->appManager->expects($this->once())->method('getAppPath')->with('theming')->willReturn(\OC::$SERVERROOT . '/theming');
  699. $file = $this->createMock(ISimpleFile::class);
  700. $file->expects($this->any())->method('getName')->willReturn('theming.css');
  701. $file->expects($this->any())->method('getMTime')->willReturn(42);
  702. $file->expects($this->any())->method('getContent')->willReturn('compiled');
  703. $this->scssCacher->expects($this->once())->method('process')->willReturn(true);
  704. $this->scssCacher->expects($this->once())->method('getCachedCSS')->willThrowException(new NotFoundException());
  705. $response = new Http\NotFoundResponse();
  706. $actual = $this->themingController->getStylesheet();
  707. $this->assertEquals($response, $actual);
  708. }
  709. public function testGetStylesheetOutsideServerroot() {
  710. $this->appManager->expects($this->once())->method('getAppPath')->with('theming')->willReturn('/outside/serverroot/theming');
  711. $file = $this->createMock(ISimpleFile::class);
  712. $file->expects($this->any())->method('getName')->willReturn('theming.css');
  713. $file->expects($this->any())->method('getMTime')->willReturn(42);
  714. $file->expects($this->any())->method('getContent')->willReturn('compiled');
  715. $this->scssCacher->expects($this->once())->method('process')->with('/outside/serverroot/theming', 'css/theming.scss', 'theming')->willReturn(true);
  716. $this->scssCacher->expects($this->once())->method('getCachedCSS')->willReturn($file);
  717. $response = new Http\FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']);
  718. $response->cacheFor(86400);
  719. $actual = $this->themingController->getStylesheet();
  720. $this->assertEquals($response, $actual);
  721. }
  722. public function testGetManifest() {
  723. $this->config
  724. ->expects($this->once())
  725. ->method('getAppValue')
  726. ->with('theming', 'cachebuster', '0')
  727. ->willReturn('0');
  728. $this->themingDefaults
  729. ->expects($this->any())
  730. ->method('getName')
  731. ->willReturn('Nextcloud');
  732. $this->urlGenerator
  733. ->expects($this->at(0))
  734. ->method('getBaseUrl')
  735. ->willReturn('localhost');
  736. $this->urlGenerator
  737. ->expects($this->at(1))
  738. ->method('linkToRoute')
  739. ->with('theming.Icon.getTouchIcon', ['app' => 'core'])
  740. ->willReturn('touchicon');
  741. $this->urlGenerator
  742. ->expects($this->at(2))
  743. ->method('linkToRoute')
  744. ->with('theming.Icon.getFavicon', ['app' => 'core'])
  745. ->willReturn('favicon');
  746. $response = new Http\JSONResponse([
  747. 'name' => 'Nextcloud',
  748. 'start_url' => 'localhost',
  749. 'icons' =>
  750. [
  751. [
  752. 'src' => 'touchicon?v=0',
  753. 'type' => 'image/png',
  754. 'sizes' => '512x512'
  755. ],
  756. [
  757. 'src' => 'favicon?v=0',
  758. 'type' => 'image/svg+xml',
  759. 'sizes' => '16x16'
  760. ]
  761. ],
  762. 'display' => 'standalone',
  763. 'short_name' => 'Nextcloud',
  764. 'theme_color' => null,
  765. 'background_color' => null,
  766. 'description' => null
  767. ]);
  768. $response->cacheFor(3600);
  769. $this->assertEquals($response, $this->themingController->getManifest('core'));
  770. }
  771. }