PageRenderTime 160ms CodeModel.GetById 19ms RepoModel.GetById 2ms app.codeStats 0ms

/tests/cases/net/http/MediaTest.php

http://github.com/UnionOfRAD/lithium
PHP | 1073 lines | 801 code | 198 blank | 74 comment | 4 complexity | c0d0224e782a7cac6abcd7812acda0f8 MD5 | raw file
  1. <?php
  2. /**
  3. * li₃: the most RAD framework for PHP (http://li3.me)
  4. *
  5. * Copyright 2009, Union of RAD. All rights reserved. This source
  6. * code is distributed under the terms of the BSD 3-Clause License.
  7. * The full license text can be found in the LICENSE.txt file.
  8. */
  9. namespace lithium\tests\cases\net\http;
  10. use lithium\core\Environment;
  11. use lithium\net\http\Media;
  12. use lithium\action\Request;
  13. use lithium\action\Response;
  14. use lithium\core\Libraries;
  15. use lithium\data\entity\Record;
  16. use lithium\data\collection\RecordSet;
  17. class MediaTest extends \lithium\test\Unit {
  18. /**
  19. * Reset the `Media` class to its default state.
  20. */
  21. public function tearDown() {
  22. Media::reset();
  23. }
  24. /**
  25. * Tests setting, getting and removing custom media types.
  26. */
  27. public function testMediaTypes() {
  28. // Get a list of all available media types:
  29. $types = Media::types(); // returns ['html', 'json', 'rss', ...];
  30. $expected = [
  31. 'html', 'htm', 'form', 'json', 'rss', 'atom', 'css', 'js', 'text', 'txt', 'xml'
  32. ];
  33. $this->assertEqual($expected, $types);
  34. $this->assertEqual($expected, Media::formats());
  35. $result = Media::type('json');
  36. $expected = ['application/json'];
  37. $this->assertEqual($expected, $result['content']);
  38. $expected = [
  39. 'cast' => true, 'encode' => 'json_encode', 'decode' => $result['options']['decode']
  40. ];
  41. $this->assertEqual($expected, $result['options']);
  42. // Add a custom media type with a custom view class:
  43. Media::type('my', 'text/x-my', [
  44. 'view' => 'my\custom\View',
  45. 'paths' => ['layout' => false]
  46. ]);
  47. $result = Media::types();
  48. $this->assertTrue(in_array('my', $result));
  49. $result = Media::type('my');
  50. $expected = ['text/x-my'];
  51. $this->assertEqual($expected, $result['content']);
  52. $expected = [
  53. 'view' => 'my\custom\View',
  54. 'paths' => [
  55. 'template' => '{:library}/views/{:controller}/{:template}.{:type}.php',
  56. 'layout' => false,
  57. 'element' => '{:library}/views/elements/{:template}.{:type}.php'
  58. ],
  59. 'encode' => null, 'decode' => null, 'cast' => true, 'conditions' => []
  60. ];
  61. $this->assertEqual($expected, $result['options']);
  62. // Remove a custom media type:
  63. Media::type('my', false);
  64. $result = Media::types();
  65. $this->assertFalse(in_array('my', $result));
  66. }
  67. /**
  68. * Tests that `Media` will return the correct type name of recognized, registered content types.
  69. */
  70. public function testContentTypeDetection() {
  71. $this->assertNull(Media::type('application/foo'));
  72. $this->assertEqual('js', Media::type('application/javascript'));
  73. $this->assertEqual('html', Media::type('*/*'));
  74. $this->assertEqual('json', Media::type('application/json'));
  75. $this->assertEqual('json', Media::type('application/json; charset=UTF-8'));
  76. $result = Media::type('json');
  77. $expected = ['content' => ['application/json'], 'options' => [
  78. 'cast' => true, 'encode' => 'json_encode', 'decode' => $result['options']['decode']
  79. ]];
  80. $this->assertEqual($expected, $result);
  81. }
  82. public function testAssetTypeHandling() {
  83. $result = Media::assets();
  84. $expected = ['js', 'css', 'image', 'generic'];
  85. $this->assertEqual($expected, array_keys($result));
  86. $result = Media::assets('css');
  87. $expected = '.css';
  88. $this->assertEqual($expected, $result['suffix']);
  89. $this->assertTrue(isset($result['paths']['{:base}/{:library}/css/{:path}']));
  90. $result = Media::assets('my');
  91. $this->assertNull($result);
  92. $result = Media::assets('my', ['suffix' => '.my', 'paths' => [
  93. '{:base}/my/{:path}' => ['base', 'path']
  94. ]]);
  95. $this->assertNull($result);
  96. $result = Media::assets('my');
  97. $expected = '.my';
  98. $this->assertEqual($expected, $result['suffix']);
  99. $this->assertTrue(isset($result['paths']['{:base}/my/{:path}']));
  100. $this->assertNull($result['filter']);
  101. Media::assets('my', ['filter' => ['/my/' => '/your/']]);
  102. $result = Media::assets('my');
  103. $expected = ['/my/' => '/your/'];
  104. $this->assertEqual($expected, $result['filter']);
  105. $expected = '.my';
  106. $this->assertEqual($expected, $result['suffix']);
  107. Media::assets('my', false);
  108. $result = Media::assets('my');
  109. $this->assertNull($result);
  110. $this->assertEqual('/foo.exe', Media::asset('foo.exe', 'bar'));
  111. }
  112. public function testAssetAbsoluteRelativePaths() {
  113. $result = Media::asset('scheme://host/subpath/file', 'js');
  114. $expected = 'scheme://host/subpath/file';
  115. $this->assertEqual($expected, $result);
  116. $result = Media::asset('//host/subpath/file', 'js', ['base' => '/base']);
  117. $expected = '//host/subpath/file';
  118. $this->assertEqual($expected, $result);
  119. $result = Media::asset('subpath/file', 'js');
  120. $expected = '/js/subpath/file.js';
  121. $this->assertEqual($expected, $result);
  122. }
  123. public function testCustomAssetUrls() {
  124. $env = Environment::get();
  125. $path = Libraries::get(true, 'path');
  126. Libraries::add('cdn_js_test', [
  127. 'path' => $path,
  128. 'assets' => [
  129. 'js' => 'http://static.cdn.com'
  130. ],
  131. 'bootstrap' => false
  132. ]);
  133. Libraries::add('cdn_env_test', [
  134. 'path' => $path,
  135. 'assets' => [
  136. 'js' => 'wrong',
  137. $env => ['js' => 'http://static.cdn.com/myapp']
  138. ],
  139. 'bootstrap' => false
  140. ]);
  141. $library = basename($path);
  142. $result = Media::asset('foo', 'js', ['library' => 'cdn_js_test']);
  143. $this->assertEqual("http://static.cdn.com/{$library}/js/foo.js", $result);
  144. $result = Media::asset('foo', 'css', ['library' => 'cdn_js_test']);
  145. $this->assertEqual("/{$library}/css/foo.css", $result);
  146. $result = Media::asset('foo', 'js', ['library' => 'cdn_env_test']);
  147. $this->assertEqual("http://static.cdn.com/myapp/{$library}/js/foo.js", $result);
  148. Libraries::remove('cdn_env_test');
  149. Libraries::remove('cdn_js_test');
  150. }
  151. public function testAssetPathGeneration() {
  152. $resources = Libraries::get(true, 'resources');
  153. $this->skipIf(!is_writable($resources), "Cannot write test app to resources directory.");
  154. $paths = ["{$resources}/media_test/webroot/css", "{$resources}/media_test/webroot/js"];
  155. foreach ($paths as $path) {
  156. if (!is_dir($path)) {
  157. mkdir($path, 0777, true);
  158. }
  159. }
  160. touch("{$paths[0]}/debug.css");
  161. Libraries::add('media_test', ['path' => "{$resources}/media_test"]);
  162. $result = Media::asset('debug', 'css', ['check' => true, 'library' => 'media_test']);
  163. $this->assertEqual('/media_test/css/debug.css', $result);
  164. $result = Media::asset('debug', 'css', [
  165. 'timestamp' => true, 'library' => 'media_test'
  166. ]);
  167. $this->assertPattern('%^/media_test/css/debug\.css\?\d+$%', $result);
  168. $result = Media::asset('debug.css?type=test', 'css', [
  169. 'check' => true, 'base' => 'foo', 'library' => 'media_test'
  170. ]);
  171. $this->assertEqual('foo/media_test/css/debug.css?type=test', $result);
  172. $result = Media::asset('debug.css?type=test', 'css', [
  173. 'check' => true, 'base' => 'foo', 'timestamp' => true, 'library' => 'media_test'
  174. ]);
  175. $this->assertPattern('%^foo/media_test/css/debug\.css\?type=test&\d+$%', $result);
  176. $file = Media::path('css/debug.css', 'bar', ['library' => 'media_test']);
  177. $this->assertFileExists($file);
  178. $result = Media::asset('this.file.should.not.exist', 'css', ['check' => true]);
  179. $this->assertFalse($result);
  180. unlink("{$paths[0]}/debug.css");
  181. foreach (array_merge($paths, [dirname($paths[0])]) as $path) {
  182. rmdir($path);
  183. }
  184. }
  185. public function testCustomAssetPathGeneration() {
  186. Media::assets('my', ['suffix' => '.my', 'paths' => [
  187. '{:base}/my/{:path}' => ['base', 'path']
  188. ]]);
  189. $result = Media::asset('subpath/file', 'my');
  190. $expected = '/my/subpath/file.my';
  191. $this->assertEqual($expected, $result);
  192. Media::assets('my', ['filter' => ['/my/' => '/your/']]);
  193. $result = Media::asset('subpath/file', 'my');
  194. $expected = '/your/subpath/file.my';
  195. $this->assertEqual($expected, $result);
  196. $result = Media::asset('subpath/file', 'my', ['base' => '/app/path']);
  197. $expected = '/app/path/your/subpath/file.my';
  198. $this->assertEqual($expected, $result);
  199. $result = Media::asset('subpath/file', 'my', ['base' => '/app/path/']);
  200. $expected = '/app/path//your/subpath/file.my';
  201. $this->assertEqual($expected, $result);
  202. }
  203. public function testMultiLibraryAssetPaths() {
  204. $result = Media::asset('path/file', 'js', ['library' => true, 'base' => '/app/base']);
  205. $expected = '/app/base/js/path/file.js';
  206. $this->assertEqual($expected, $result);
  207. Libraries::add('li3_foo_blog', [
  208. 'path' => Libraries::get(true, 'path') . '/libraries/plugins/blog',
  209. 'bootstrap' => false,
  210. 'route' => false
  211. ]);
  212. $result = Media::asset('path/file', 'js', [
  213. 'library' => 'li3_foo_blog', 'base' => '/app/base'
  214. ]);
  215. $expected = '/app/base/blog/js/path/file.js';
  216. $this->assertEqual($expected, $result);
  217. Libraries::remove('li3_foo_blog');
  218. }
  219. public function testManualAssetPaths() {
  220. $result = Media::asset('/path/file', 'js', ['base' => '/base']);
  221. $expected = '/base/path/file.js';
  222. $this->assertEqual($expected, $result);
  223. $resources = Libraries::get(true, 'resources');
  224. $cssPath = "{$resources}/media_test/webroot/css";
  225. $this->skipIf(!is_writable($resources), "Cannot write test app to resources directory.");
  226. if (!is_dir($cssPath)) {
  227. mkdir($cssPath, 0777, true);
  228. }
  229. Libraries::add('media_test', ['path' => "{$resources}/media_test"]);
  230. $result = Media::asset('/foo/bar', 'js', ['base' => '/base', 'check' => true]);
  231. $this->assertFalse($result);
  232. file_put_contents("{$cssPath}/debug.css", "html, body { background-color: black; }");
  233. $result = Media::asset('/css/debug', 'css', [
  234. 'library' => 'media_test', 'base' => '/base', 'check' => true
  235. ]);
  236. $expected = '/base/css/debug.css';
  237. $this->assertEqual($expected, $result);
  238. $result = Media::asset('/css/debug.css', 'css', [
  239. 'library' => 'media_test', 'base' => '/base', 'check' => true
  240. ]);
  241. $expected = '/base/css/debug.css';
  242. $this->assertEqual($expected, $result);
  243. $result = Media::asset('/css/debug.css?foo', 'css', [
  244. 'library' => 'media_test', 'base' => '/base', 'check' => true
  245. ]);
  246. $expected = '/base/css/debug.css?foo';
  247. $this->assertEqual($expected, $result);
  248. Libraries::remove('media_test');
  249. unlink("{$cssPath}/debug.css");
  250. foreach ([$cssPath, dirname($cssPath)] as $path) {
  251. rmdir($path);
  252. }
  253. }
  254. public function testRender() {
  255. $response = new Response();
  256. $response->type('json');
  257. $data = ['something'];
  258. Media::render($response, $data);
  259. $result = $response->headers();
  260. $this->assertEqual(['Content-Type: application/json; charset=UTF-8'], $result);
  261. $result = $response->body();
  262. $this->assertEqual($data, $result);
  263. }
  264. /**
  265. * Tests that a decode handler is not called when the Media type has none configured.
  266. */
  267. public function testNoDecode() {
  268. Media::type('my', 'text/x-my', ['decode' => false]);
  269. $result = Media::decode('my', 'Hello World');
  270. $this->assertEqual(null, $result);
  271. }
  272. /**
  273. * Tests that types with decode handlers can properly decode content.
  274. */
  275. public function testDecode() {
  276. $data = ['movies' => [
  277. ['name' => 'Shaun of the Dead', 'year' => 2004],
  278. ['name' => 'V for Vendetta', 'year' => 2005]
  279. ]];
  280. $jsonEncoded = '{"movies":[{"name":"Shaun of the Dead","year":2004},';
  281. $jsonEncoded .= '{"name":"V for Vendetta","year":2005}]}';
  282. $result = Media::decode('json', $jsonEncoded);
  283. $this->assertEqual($data, $result);
  284. $formEncoded = 'movies%5B0%5D%5Bname%5D=Shaun+of+the+Dead&movies%5B0%5D%5Byear%5D=2004';
  285. $formEncoded .= '&movies%5B1%5D%5Bname%5D=V+for+Vendetta&movies%5B1%5D%5Byear%5D=2005';
  286. $result = Media::decode('form', $formEncoded);
  287. $this->assertEqual($data, $result);
  288. }
  289. public function testCustomEncodeHandler() {
  290. $response = new Response();
  291. Media::type('csv', 'application/csv', [
  292. 'encode' => function($data) {
  293. ob_start();
  294. $out = fopen('php://output', 'w');
  295. foreach ($data as $record) {
  296. fputcsv($out, $record);
  297. }
  298. fclose($out);
  299. return ob_get_clean();
  300. }
  301. ]);
  302. $data = [
  303. ['John', 'Doe', '123 Main St.', 'Anytown, CA', '91724'],
  304. ['Jane', 'Doe', '124 Main St.', 'Anytown, CA', '91724']
  305. ];
  306. $response->type('csv');
  307. Media::render($response, $data);
  308. $result = $response->body;
  309. $expected = 'John,Doe,"123 Main St.","Anytown, CA",91724' . "\n";
  310. $expected .= 'Jane,Doe,"124 Main St.","Anytown, CA",91724' . "\n";
  311. $this->assertEqual([$expected], $result);
  312. $result = $response->headers['Content-Type'];
  313. $this->assertEqual('application/csv; charset=UTF-8', $result);
  314. }
  315. public function testEmptyEncode() {
  316. $handler = Media::type('empty', 'empty/encode');
  317. $this->assertNull(Media::encode($handler, []));
  318. $handler = Media::type('empty', 'empty/encode', [
  319. 'encode' => null
  320. ]);
  321. $this->assertNull(Media::encode($handler, []));
  322. $handler = Media::type('empty', 'empty/encode', [
  323. 'encode' => false
  324. ]);
  325. $this->assertNull(Media::encode($handler, []));
  326. $handler = Media::type('empty', 'empty/encode', [
  327. 'encode' => ""
  328. ]);
  329. $this->assertNull(Media::encode($handler, []));
  330. }
  331. /**
  332. * Tests that rendering plain text correctly returns the render data as-is.
  333. */
  334. public function testPlainTextOutput() {
  335. $response = new Response();
  336. $response->type('text');
  337. Media::render($response, "Hello, world!");
  338. $result = $response->body;
  339. $this->assertEqual(["Hello, world!"], $result);
  340. }
  341. /**
  342. * Tests that an exception is thrown for cases where an attempt is made to render content for
  343. * a type which is not registered.
  344. */
  345. public function testUndhandledContent() {
  346. $response = new Response();
  347. $response->type('bad');
  348. $this->assertException("Unhandled media type `bad`.", function() use ($response) {
  349. Media::render($response, ['foo' => 'bar']);
  350. });
  351. $result = $response->body();
  352. $this->assertIdentical('', $result);
  353. }
  354. /**
  355. * Tests that attempts to render a media type with no handler registered produces an
  356. * 'unhandled media type' exception, even if the type itself is a registered content type.
  357. */
  358. public function testUnregisteredContentHandler() {
  359. $response = new Response();
  360. $response->type('xml');
  361. $this->assertException("Unhandled media type `xml`.", function() use ($response) {
  362. Media::render($response, ['foo' => 'bar']);
  363. });
  364. $result = $response->body;
  365. $this->assertNull($result);
  366. }
  367. /**
  368. * Tests handling content type manually using parameters to `Media::render()`, for content types
  369. * that are registered but have no default handler.
  370. */
  371. public function testManualContentHandling() {
  372. Media::type('custom', 'text/x-custom');
  373. $response = new Response();
  374. $response->type('custom');
  375. Media::render($response, 'Hello, world!', [
  376. 'layout' => false,
  377. 'template' => false,
  378. 'encode' => function($data) { return "Message: {$data}"; }
  379. ]);
  380. $result = $response->body;
  381. $expected = ["Message: Hello, world!"];
  382. $this->assertEqual($expected, $result);
  383. $this->assertException("/Template not found/", function() use ($response) {
  384. Media::render($response, 'Hello, world!');
  385. });
  386. }
  387. /**
  388. * Tests that parameters from the `Request` object passed into `render()` via
  389. * `$options['request']` are properly merged into the `$options` array passed to render
  390. * handlers.
  391. */
  392. public function testRequestOptionMerging() {
  393. Media::type('custom', 'text/x-custom');
  394. $request = new Request();
  395. $request->params['foo'] = 'bar';
  396. $response = new Response();
  397. $response->type('custom');
  398. Media::render($response, null, compact('request') + [
  399. 'layout' => false,
  400. 'template' => false,
  401. 'encode' => function($data, $handler) { return $handler['request']->foo; }
  402. ]);
  403. $this->assertEqual(['bar'], $response->body);
  404. }
  405. public function testMediaEncoding() {
  406. $data = ['hello', 'goodbye', 'foo' => ['bar', 'baz' => 'dib']];
  407. $expected = json_encode($data);
  408. $result = Media::encode('json', $data);
  409. $this->assertEqual($expected, $result);
  410. $this->assertEqual($result, Media::to('json', $data));
  411. $this->assertNull(Media::encode('badness', $data));
  412. $result = Media::decode('json', $expected);
  413. $this->assertEqual($data, $result);
  414. }
  415. public function testRenderWithOptionsMerging() {
  416. $base = Libraries::get(true, 'resources') . '/tmp';
  417. $this->skipIf(!is_writable($base), "Path `{$base}` is not writable.");
  418. $request = new Request();
  419. $request->params['controller'] = 'pages';
  420. $response = new Response();
  421. $response->type('html');
  422. $this->assertException("/Template not found/", function() use ($response) {
  423. Media::render($response, null);
  424. });
  425. $this->_cleanUp();
  426. }
  427. public function testCustomWebroot() {
  428. Libraries::add('defaultStyleApp', [
  429. 'path' => Libraries::get(true, 'path'),
  430. 'bootstrap' => false]
  431. );
  432. $this->assertEqual(
  433. realpath(Libraries::get(true, 'path') . '/webroot'),
  434. realpath(Media::webroot('defaultStyleApp'))
  435. );
  436. Libraries::add('customWebRootApp', [
  437. 'path' => Libraries::get(true, 'path'),
  438. 'webroot' => Libraries::get(true, 'path'),
  439. 'bootstrap' => false
  440. ]);
  441. $this->assertEqual(Libraries::get(true, 'path'), Media::webroot('customWebRootApp'));
  442. Libraries::remove('defaultStyleApp');
  443. Libraries::remove('customWebRootApp');
  444. $this->assertNull(Media::webroot('defaultStyleApp'));
  445. }
  446. /**
  447. * Tests that the `Media` class' configuration can be reset to its default state.
  448. */
  449. public function testStateReset() {
  450. $this->assertFalse(in_array('foo', Media::types()));
  451. Media::type('foo', 'text/x-foo');
  452. $this->assertTrue(in_array('foo', Media::types()));
  453. Media::reset();
  454. $this->assertFalse(in_array('foo', Media::types()));
  455. }
  456. public function testEncodeRecordSet() {
  457. $data = new RecordSet(['data' => [
  458. 1 => new Record(['data' => ['id' => 1, 'foo' => 'bar']]),
  459. 2 => new Record(['data' => ['id' => 2, 'foo' => 'baz']]),
  460. 3 => new Record(['data' => ['id' => 3, 'baz' => 'dib']])
  461. ]]);
  462. $json = '{"1":{"id":1,"foo":"bar"},"2":{"id":2,"foo":"baz"},"3":{"id":3,"baz":"dib"}}';
  463. $this->assertEqual($json, Media::encode(['encode' => 'json_encode'], $data));
  464. }
  465. public function testEncodeNotCallable() {
  466. $data = ['foo' => 'bar'];
  467. $result = Media::encode(['encode' => false], $data);
  468. $this->assertNull($result);
  469. }
  470. /**
  471. * Tests that calling `Media::type()` to retrieve the details of a type that is aliased to
  472. * another type, automatically resolves to the settings of the type being pointed at.
  473. */
  474. public function testTypeAliasResolution() {
  475. $resolved = Media::type('text');
  476. $this->assertEqual(['text/plain'], $resolved['content']);
  477. unset($resolved['options']['encode']);
  478. $result = Media::type('txt');
  479. unset($result['options']['encode']);
  480. $this->assertEqual($resolved, $result);
  481. }
  482. public function testQueryUndefinedAssetTypes() {
  483. $base = Media::path('index.php', 'generic');
  484. $result = Media::path('index.php', 'foo');
  485. $this->assertEqual($result, $base);
  486. $base = Media::asset('/bar', 'generic');
  487. $result = Media::asset('/bar', 'foo');
  488. $this->assertEqual($result, $base);
  489. }
  490. public function testGetLibraryWebroot() {
  491. $this->assertNull(Media::webroot('foobar'));
  492. Libraries::add('foobar', ['path' => __DIR__, 'webroot' => __DIR__]);
  493. $this->assertEqual(__DIR__, Media::webroot('foobar'));
  494. Libraries::remove('foobar');
  495. $resources = Libraries::get(true, 'resources');
  496. $webroot = "{$resources}/media_test/webroot";
  497. $this->skipIf(!is_writable($resources), "Cannot write test app to resources directory.");
  498. if (!is_dir($webroot)) {
  499. mkdir($webroot, 0777, true);
  500. }
  501. Libraries::add('media_test', ['path' => "{$resources}/media_test"]);
  502. $this->assertFileExists(Media::webroot('media_test'));
  503. Libraries::remove('media_test');
  504. rmdir($webroot);
  505. }
  506. /**
  507. * Tests that the `Response` object can be directly modified from a templating class or encode
  508. * function.
  509. */
  510. public function testResponseModification() {
  511. Media::type('my', 'text/x-my', ['view' => 'lithium\tests\mocks\net\http\Template']);
  512. $response = new Response();
  513. Media::render($response, null, ['type' => 'my']);
  514. $this->assertEqual('Value', $response->headers('Custom'));
  515. }
  516. /**
  517. * Tests that `Media::asset()` will not prepend path strings with the base application path if
  518. * it has already been prepended.
  519. */
  520. public function testDuplicateBasePathCheck() {
  521. $result = Media::asset('/foo/bar/image.jpg', 'image', ['base' => '/bar']);
  522. $this->assertEqual('/bar/foo/bar/image.jpg', $result);
  523. $result = Media::asset('/foo/bar/image.jpg', 'image', ['base' => '/foo/bar']);
  524. $this->assertEqual('/foo/bar/image.jpg', $result);
  525. $result = Media::asset('foo/bar/image.jpg', 'image', ['base' => 'foo']);
  526. $this->assertEqual('foo/img/foo/bar/image.jpg', $result);
  527. $result = Media::asset('/foo/bar/image.jpg', 'image', ['base' => '']);
  528. $this->assertEqual('/foo/bar/image.jpg', $result);
  529. }
  530. public function testContentNegotiationSimple() {
  531. $request = new Request(['env' => [
  532. 'HTTP_ACCEPT' => 'text/html,text/plain;q=0.5'
  533. ]]);
  534. $this->assertEqual('html', Media::negotiate($request));
  535. $request = new Request(['env' => [
  536. 'HTTP_ACCEPT' => 'application/json'
  537. ]]);
  538. $this->assertEqual('json', Media::negotiate($request));
  539. }
  540. public function testContentNegotiationByType() {
  541. $this->assertEqual('html', Media::type('text/html'));
  542. Media::type('jsonp', 'text/html', [
  543. 'conditions' => ['type' => true]
  544. ]);
  545. $this->assertEqual(['jsonp', 'html'], Media::type('text/html'));
  546. $config = ['env' => ['HTTP_ACCEPT' => 'text/html,text/plain;q=0.5']];
  547. $request = new Request($config);
  548. $request->params = ['type' => 'jsonp'];
  549. $this->assertEqual('jsonp', Media::negotiate($request));
  550. $request = new Request($config);
  551. $this->assertEqual('html', Media::negotiate($request));
  552. }
  553. public function testContentNegotiationByUserAgent() {
  554. Media::type('iphone', 'application/xhtml+xml', [
  555. 'conditions' => ['mobile' => true]
  556. ]);
  557. $request = new Request(['env' => [
  558. 'HTTP_USER_AGENT' => 'Safari',
  559. 'HTTP_ACCEPT' => 'application/xhtml+xml,text/html'
  560. ]]);
  561. $this->assertEqual('html', Media::negotiate($request));
  562. $request = new Request(['env' => [
  563. 'HTTP_USER_AGENT' => 'iPhone',
  564. 'HTTP_ACCEPT' => 'application/xhtml+xml,text/html'
  565. ]]);
  566. $this->assertEqual('iphone', Media::negotiate($request));
  567. }
  568. /**
  569. * Tests that empty asset paths correctly return the base path for the asset type, and don't
  570. * generate notices or errors.
  571. */
  572. public function testEmptyAssetPaths() {
  573. $this->assertEqual('/img/', Media::asset('', 'image'));
  574. $this->assertEqual('/css/.css', Media::asset('', 'css'));
  575. $this->assertEqual('/js/.js', Media::asset('', 'js'));
  576. $this->assertEqual('/', Media::asset('', 'generic'));
  577. }
  578. public function testLocation() {
  579. $webroot = Libraries::get(true, 'resources') . '/tmp/tests/webroot';
  580. mkdir($webroot, 0777, true);
  581. $webroot = realpath($webroot);
  582. $this->assertNotEmpty($webroot);
  583. Media::attach('tests', [
  584. 'absolute' => true,
  585. 'host' => 'www.hostname.com',
  586. 'scheme' => 'http://',
  587. 'prefix' => '/web/assets/tests',
  588. 'path' => $webroot
  589. ]);
  590. Media::attach('app', [
  591. 'absolute' => false,
  592. 'prefix' => '/web/assets/app',
  593. 'path' => $webroot
  594. ]);
  595. $expected = [
  596. 'absolute' => false,
  597. 'host' => 'localhost',
  598. 'scheme' => 'http://',
  599. 'base' => null,
  600. 'prefix' => 'web/assets/app',
  601. 'path' => $webroot,
  602. 'timestamp' => false,
  603. 'filter' => null,
  604. 'suffix' => null,
  605. 'check' => false
  606. ];
  607. $result = Media::attached('app');
  608. $this->assertEqual($expected, $result);
  609. $expected = [
  610. 'absolute' => true,
  611. 'host' => 'www.hostname.com',
  612. 'scheme' => 'http://',
  613. 'base' => null,
  614. 'prefix' => 'web/assets/tests',
  615. 'path' => $webroot,
  616. 'timestamp' => false,
  617. 'filter' => null,
  618. 'suffix' => null,
  619. 'check' => false
  620. ];
  621. $result = Media::attached('tests');
  622. $this->assertEqual($expected, $result);
  623. $this->_cleanUp();
  624. }
  625. public function testAssetWithAbsoluteLocation() {
  626. Media::attach('appcdn', [
  627. 'absolute' => true,
  628. 'scheme' => 'http://',
  629. 'host' => 'my.cdn.com',
  630. 'prefix' => '/assets',
  631. 'path' => null
  632. ]);
  633. $result = Media::asset('style','css');
  634. $expected = '/css/style.css';
  635. $this->assertEqual($expected, $result);
  636. $result = Media::asset('style','css', ['scope' => 'appcdn']);
  637. $expected = 'http://my.cdn.com/assets/css/style.css';
  638. $this->assertEqual($expected, $result);
  639. Media::scope('appcdn');
  640. $result = Media::asset('style', 'css', ['scope' => false]);
  641. $expected = '/css/style.css';
  642. $this->assertEqual($expected, $result);
  643. $result = Media::asset('style', 'css');
  644. $expected = 'http://my.cdn.com/assets/css/style.css';
  645. $this->assertEqual($expected, $result);
  646. }
  647. /**
  648. * Create environment prefix location using `lihtium\net\http\Media::location`
  649. * Check if `lihtium\net\http\Media::asset` return the correct URL
  650. * for the production environement
  651. */
  652. public function testEnvironmentAsset1() {
  653. Media::attach('appcdn', [
  654. 'production' => [
  655. 'absolute' => true,
  656. 'path' => null,
  657. 'scheme' => 'http://',
  658. 'host' => 'my.cdnapp.com',
  659. 'prefix' => '/assets',
  660. ],
  661. 'test' => [
  662. 'absolute' => true,
  663. 'path' => null,
  664. 'scheme' => 'http://',
  665. 'host' => 'my.cdntest.com',
  666. 'prefix' => '/assets',
  667. ]
  668. ]);
  669. $env = Environment::get();
  670. Environment::set('production');
  671. $result = Media::asset('style', 'css', ['scope' => 'appcdn']);
  672. $expected = 'http://my.cdnapp.com/assets/css/style.css';
  673. }
  674. /**
  675. * Create environment prefix location using `lihtium\net\http\Media::location`
  676. * Check if `lihtium\net\http\Media::asset` return the correct URL
  677. * for the test environement
  678. */
  679. public function testEnvironmentAsset2() {
  680. Media::attach('appcdn', [
  681. 'production' => [
  682. 'absolute' => true,
  683. 'path' => null,
  684. 'scheme' => 'http://',
  685. 'host' => 'my.cdnapp.com',
  686. 'prefix' => 'assets',
  687. ],
  688. 'test' => [
  689. 'absolute' => true,
  690. 'path' => null,
  691. 'scheme' => 'http://',
  692. 'host' => 'my.cdntest.com',
  693. 'prefix' => 'assets',
  694. ]
  695. ]);
  696. $env = Environment::get();
  697. Environment::set('test');
  698. $result = Media::asset('style', 'css', ['scope' => 'appcdn']);
  699. $expected = 'http://my.cdntest.com/assets/css/style.css';
  700. $this->assertEqual($expected, $result);
  701. Environment::is($env);
  702. }
  703. public function testAssetPathGenerationWithLocation() {
  704. $resources = Libraries::get(true, 'resources') . '/tmp/tests';
  705. $this->skipIf(!is_writable($resources), "Cannot write test app to resources directory.");
  706. $paths = ["{$resources}/media_test/css", "{$resources}/media_test/js"];
  707. foreach ($paths as $path) {
  708. if (!is_dir($path)) {
  709. mkdir($path, 0777, true);
  710. }
  711. }
  712. touch("{$paths[0]}/debug.css");
  713. Media::attach('media_test', [
  714. 'prefix' => '',
  715. 'path' => "{$resources}/media_test"]
  716. );
  717. $result = Media::asset('debug', 'css', ['check' => true, 'scope' => 'media_test']);
  718. $this->assertEqual('/css/debug.css', $result);
  719. Media::attach('media_test', [
  720. 'prefix' => 'media_test',
  721. 'path' => "{$resources}/media_test"]
  722. );
  723. $result = Media::asset('debug', 'css', ['check' => true, 'scope' => 'media_test']);
  724. $this->assertEqual('/media_test/css/debug.css', $result);
  725. $result = Media::asset('debug', 'css', [
  726. 'timestamp' => true, 'scope' => 'media_test'
  727. ]);
  728. $this->assertPattern('%^/media_test/css/debug\.css\?\d+$%', $result);
  729. $result = Media::asset('/css/debug.css?type=test', 'css', [
  730. 'check' => true, 'base' => 'foo', 'scope' => 'media_test'
  731. ]);
  732. $this->assertEqual('foo/media_test/css/debug.css?type=test', $result);
  733. $result = Media::asset('/css/debug.css?type=test', 'css', [
  734. 'check' => true, 'base' => 'http://www.hostname.com/foo', 'scope' => 'media_test'
  735. ]);
  736. $expected = 'http://www.hostname.com/foo/media_test/css/debug.css?type=test';
  737. $this->assertEqual($expected, $result);
  738. $result = Media::asset('/css/debug.css?type=test', 'css', [
  739. 'check' => true, 'base' => 'foo', 'timestamp' => true, 'scope' => 'media_test'
  740. ]);
  741. $this->assertPattern('%^foo/media_test/css/debug\.css\?type=test&\d+$%', $result);
  742. $result = Media::asset('this.file.should.not.exist.css', 'css', ['check' => true]);
  743. $this->assertFalse($result);
  744. Media::attach('media_test', [
  745. 'prefix' => 'media_test',
  746. 'path' => "{$resources}/media_test"]
  747. );
  748. $result = Media::asset('debug', 'css', ['check' => true, 'scope' => 'media_test']);
  749. $this->assertEqual('/media_test/css/debug.css', $result);
  750. $result = Media::asset('debug', 'css', [
  751. 'timestamp' => true, 'scope' => 'media_test'
  752. ]);
  753. $this->assertPattern('%^/media_test/css/debug\.css\?\d+$%', $result);
  754. $result = Media::asset('/css/debug.css?type=test', 'css', [
  755. 'check' => true, 'base' => 'foo', 'scope' => 'media_test'
  756. ]);
  757. $this->assertEqual('foo/media_test/css/debug.css?type=test', $result);
  758. $result = Media::asset('/css/debug.css?type=test', 'css', [
  759. 'check' => true, 'base' => 'foo', 'timestamp' => true, 'scope' => 'media_test'
  760. ]);
  761. $this->assertPattern('%^foo/media_test/css/debug\.css\?type=test&\d+$%', $result);
  762. $result = Media::asset('this.file.should.not.exist.css', 'css', ['check' => true]);
  763. $this->assertFalse($result);
  764. unlink("{$paths[0]}/debug.css");
  765. foreach (array_merge($paths, [dirname($paths[0])]) as $path) {
  766. rmdir($path);
  767. }
  768. }
  769. public function testEmptyHostAndSchemeOptionLocation() {
  770. Media::attach('app', ['absolute' => true]);
  771. Media::scope('app');
  772. $result = Media::asset('/js/path/file', 'js', ['base' => '/app/base']);
  773. $expected = 'http://localhost/app/base/js/path/file.js';
  774. $this->assertEqual($expected, $result);
  775. }
  776. public function testDeleteLocation() {
  777. $result = Media::asset('/js/path/file', 'js', ['base' => '/app/base']);
  778. $expected = '/app/base/js/path/file.js';
  779. $this->assertEqual($expected, $result);
  780. Media::attach('foo_blog', [
  781. 'prefix' => 'assets/plugin/blog'
  782. ]);
  783. $result = Media::asset('/js/path/file', 'js', [
  784. 'scope' => 'foo_blog', 'base' => '/app/base'
  785. ]);
  786. $expected = '/app/base/assets/plugin/blog/js/path/file.js';
  787. $this->assertEqual($expected, $result);
  788. Media::attach('foo_blog', false);
  789. $this->assertEqual([], Media::attached('foo_blog'));
  790. }
  791. public function testListAttached() {
  792. Media::attach('media1', ['prefix' => 'media1', 'absolute' => true]);
  793. Media::attach('media2', ['prefix' => 'media2', 'check' => true]);
  794. Media::attach('media3', ['prefix' => 'media3']);
  795. $expected = [
  796. 'media1' => [
  797. 'prefix' => 'media1',
  798. 'absolute' => true,
  799. 'host' => 'localhost',
  800. 'scheme' => 'http://',
  801. 'base' => null,
  802. 'path' => null,
  803. 'timestamp' => false,
  804. 'filter' => null,
  805. 'suffix' => null,
  806. 'check' => false
  807. ],
  808. 'media2' => [
  809. 'prefix' => 'media2',
  810. 'absolute' => false,
  811. 'host' => 'localhost',
  812. 'scheme' => 'http://',
  813. 'base' => null,
  814. 'path' => null,
  815. 'timestamp' => false,
  816. 'filter' => null,
  817. 'suffix' => null,
  818. 'check' => true
  819. ],
  820. 'media3' => [
  821. 'prefix' => 'media3',
  822. 'absolute' => false,
  823. 'host' => 'localhost',
  824. 'scheme' => 'http://',
  825. 'base' => null,
  826. 'path' => null,
  827. 'timestamp' => false,
  828. 'filter' => null,
  829. 'suffix' => null,
  830. 'check' => false
  831. ]
  832. ];
  833. $this->assertEqual($expected, Media::attached());
  834. }
  835. public function testMultipleHostsAndSchemeSelectSameIndex() {
  836. Media::attach('cdn', [
  837. 'absolute' => true,
  838. 'host' => ['cdn.com', 'cdn.org'],
  839. 'scheme' => ['http://', 'https://'],
  840. ]);
  841. $result = Media::asset('style.css', 'css', ['scope' => 'cdn']);
  842. $expected = '%https://cdn.org/css/style.css|http://cdn.com/css/style.css%';
  843. $this->assertPattern($expected, $result);
  844. }
  845. public function testMultipleHostsAndSingleSchemePicksOnlyScheme() {
  846. Media::attach('cdn', [
  847. 'absolute' => true,
  848. 'host' => ['cdn.com', 'cdn.org'],
  849. 'scheme' => 'http://',
  850. ]);
  851. $result = Media::asset('style.css', 'css', ['scope' => 'cdn']);
  852. $expected = '%http://cdn.org/css/style.css|http://cdn.com/css/style.css%';
  853. $this->assertPattern($expected, $result);
  854. }
  855. public function testMultipleHostsPickSameHostForIdenticalAsset() {
  856. Media::attach('cdn', [
  857. 'absolute' => true,
  858. 'host' => ['cdn.com', 'cdn.org'],
  859. 'scheme' => 'http://',
  860. ]);
  861. $first = Media::asset('style.css', 'css', ['scope' => 'cdn']);
  862. $second = Media::asset('style.css', 'css', ['scope' => 'cdn']);
  863. $third = Media::asset('style.css', 'css', ['scope' => 'cdn']);
  864. $this->assertIdentical($first, $second);
  865. $this->assertIdentical($third, $second);
  866. }
  867. public function testScopeBase() {
  868. $result = Media::asset('style.css', 'css');
  869. $this->assertEqual('/css/style.css', $result);
  870. Media::attach(false, ['base' => 'lithium/app/webroot']);
  871. $result = Media::asset('style.css', 'css');
  872. $this->assertEqual('/lithium/app/webroot/css/style.css', $result);
  873. }
  874. }
  875. ?>