Browse Source

Merge pull request #4234 from cakephp/3.0-url-helper

3.0 url helper
José Lorenzo Rodríguez 11 years ago
parent
commit
928adf7684

+ 0 - 148
src/View/Helper.php

@@ -157,154 +157,6 @@ class Helper implements EventListener {
 	}
 
 /**
- * Finds URL for specified action.
- *
- * Returns a URL pointing at the provided parameters.
- *
- * @param string|array $url Either a relative string url like `/products/view/23` or
- *    an array of URL parameters. Using an array for URLs will allow you to leverage
- *    the reverse routing features of CakePHP.
- * @param bool $full If true, the full base URL will be prepended to the result
- * @return string Full translated URL with base path.
- * @link http://book.cakephp.org/2.0/en/views/helpers.html
- */
-	public function url($url = null, $full = false) {
-		return h(Router::url($url, $full));
-	}
-
-/**
- * Checks if a file exists when theme is used, if no file is found default location is returned
- *
- * @param string $file The file to create a webroot path to.
- * @return string Web accessible path to file.
- */
-	public function webroot($file) {
-		$asset = explode('?', $file);
-		$asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
-		$webPath = $this->request->webroot . $asset[0];
-		$file = $asset[0];
-
-		if (!empty($this->theme)) {
-			$file = trim($file, '/');
-			$theme = Inflector::underscore($this->theme) . '/';
-
-			if (DS === '\\') {
-				$file = str_replace('/', '\\', $file);
-			}
-
-			if (file_exists(Configure::read('App.www_root') . $theme . $file)) {
-				$webPath = $this->request->webroot . $theme . $asset[0];
-			} else {
-				$themePath = Plugin::path($this->theme);
-				$path = $themePath . 'webroot/' . $file;
-				if (file_exists($path)) {
-					$webPath = $this->request->webroot . $theme . $asset[0];
-				}
-			}
-		}
-		if (strpos($webPath, '//') !== false) {
-			return str_replace('//', '/', $webPath . $asset[1]);
-		}
-		return $webPath . $asset[1];
-	}
-
-/**
- * Generate URL for given asset file. Depending on options passed provides full URL with domain name.
- * Also calls Helper::assetTimestamp() to add timestamp to local files
- *
- * @param string|array $path Path string or URL array
- * @param array $options Options array. Possible keys:
- *   `fullBase` Return full URL with domain name
- *   `pathPrefix` Path prefix for relative URLs
- *   `ext` Asset extension to append
- *   `plugin` False value will prevent parsing path as a plugin
- * @return string Generated URL
- */
-	public function assetUrl($path, array $options = array()) {
-		if (is_array($path)) {
-			return $this->url($path, !empty($options['fullBase']));
-		}
-		if (strpos($path, '://') !== false) {
-			return $path;
-		}
-		if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
-			list($plugin, $path) = $this->_View->pluginSplit($path, false);
-		}
-		if (!empty($options['pathPrefix']) && $path[0] !== '/') {
-			$path = $options['pathPrefix'] . $path;
-		}
-		if (
-			!empty($options['ext']) &&
-			strpos($path, '?') === false &&
-			substr($path, -strlen($options['ext'])) !== $options['ext']
-		) {
-			$path .= $options['ext'];
-		}
-		if (preg_match('|^([a-z0-9]+:)?//|', $path)) {
-			return $path;
-		}
-		if (isset($plugin)) {
-			$path = Inflector::underscore($plugin) . '/' . $path;
-		}
-		$path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path)));
-
-		if (!empty($options['fullBase'])) {
-			$path = rtrim(Router::fullBaseUrl(), '/') . '/' . ltrim($path, '/');
-		}
-		return $path;
-	}
-
-/**
- * Encodes a URL for use in HTML attributes.
- *
- * @param string $url The URL to encode.
- * @return string The URL encoded for both URL & HTML contexts.
- */
-	protected function _encodeUrl($url) {
-		$path = parse_url($url, PHP_URL_PATH);
-		$parts = array_map('rawurldecode', explode('/', $path));
-		$parts = array_map('rawurlencode', $parts);
-		$encoded = implode('/', $parts);
-		return h(str_replace($path, $encoded, $url));
-	}
-
-/**
- * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
- * Configure. If Asset.timestamp is true and debug is true, or Asset.timestamp === 'force'
- * a timestamp will be added.
- *
- * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
- * @return string Path with a timestamp added, or not.
- */
-	public function assetTimestamp($path) {
-		$stamp = Configure::read('Asset.timestamp');
-		$timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug'));
-		if ($timestampEnabled && strpos($path, '?') === false) {
-			$filepath = preg_replace(
-				'/^' . preg_quote($this->request->webroot, '/') . '/',
-				'',
-				urldecode($path)
-			);
-			$webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
-			if (file_exists($webrootPath)) {
-				//@codingStandardsIgnoreStart
-				return $path . '?' . @filemtime($webrootPath);
-				//@codingStandardsIgnoreEnd
-			}
-			$segments = explode('/', ltrim($filepath, '/'));
-			$plugin = Inflector::camelize($segments[0]);
-			if (Plugin::loaded($plugin)) {
-				unset($segments[0]);
-				$pluginPath = Plugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
-				//@codingStandardsIgnoreStart
-				return $path . '?' . @filemtime($pluginPath);
-				//@codingStandardsIgnoreEnd
-			}
-		}
-		return $path;
-	}
-
-/**
  * Returns a string to be used as onclick handler for confirm dialogs.
  *
  * @param string $message Message to be displayed

+ 6 - 6
src/View/Helper/FormHelper.php

@@ -51,7 +51,7 @@ class FormHelper extends Helper {
  *
  * @var array
  */
-	public $helpers = array('Html');
+	public $helpers = ['Url', 'Html'];
 
 /**
  * The various pickers that make up a datetime picker.
@@ -304,7 +304,7 @@ class FormHelper extends Helper {
 		unset($options['templates']);
 
 		$url = $this->_formUrl($context, $options);
-		$action = $this->url($url);
+		$action = $this->Url->build($url);
 		unset($options['url'], $options['action'], $options['idPrefix']);
 
 		$this->_lastAction($url);
@@ -1459,7 +1459,7 @@ class FormHelper extends Helper {
 
 		$formName = str_replace('.', '', uniqid('post_', true));
 		$formOptions = array(
-			'action' => $this->url($url),
+			'action' => $this->Url->build($url),
 			'name' => $formName,
 			'style' => 'display:none;',
 			'method' => 'post',
@@ -1563,11 +1563,11 @@ class FormHelper extends Helper {
 			$options['src'] = $caption;
 		} elseif ($isImage) {
 			if ($caption{0} !== '/') {
-				$url = $this->webroot(Configure::read('App.imageBaseUrl') . $caption);
+				$url = $this->Url->webroot(Configure::read('App.imageBaseUrl') . $caption);
 			} else {
-				$url = $this->webroot(trim($caption, '/'));
+				$url = $this->Url->webroot(trim($caption, '/'));
 			}
-			$url = $this->assetTimestamp($url);
+			$url = $this->Url->assetTimestamp($url);
 			$options['src'] = $url;
 		} else {
 			$options['value'] = $caption;

+ 18 - 11
src/View/Helper/HtmlHelper.php

@@ -32,6 +32,13 @@ class HtmlHelper extends Helper {
 	use StringTemplateTrait;
 
 /**
+ * List of helpers used by this helper
+ *
+ * @var array
+ */
+	public $helpers = ['Url'];
+
+/**
  * Reference to the Response object
  *
  * @var \Cake\Network\Response
@@ -237,7 +244,7 @@ class HtmlHelper extends Helper {
 		$out = null;
 
 		if (isset($options['link'])) {
-			$options['link'] = $this->assetUrl($options['link']);
+			$options['link'] = $this->Url->assetUrl($options['link']);
 			if (isset($options['rel']) && $options['rel'] === 'icon') {
 				$out = $this->formatTemplate('metalink', [
 					'url' => $options['link'],
@@ -286,7 +293,7 @@ class HtmlHelper extends Helper {
  *
  * If $url starts with "http://" this is treated as an external link. Else,
  * it is treated as a path to controller/action and parsed with the
- * HtmlHelper::url() method.
+ * UrlHelper::url() method.
  *
  * If the $url is empty, $title is used instead.
  *
@@ -307,9 +314,9 @@ class HtmlHelper extends Helper {
 	public function link($title, $url = null, array $options = array()) {
 		$escapeTitle = true;
 		if ($url !== null) {
-			$url = $this->url($url);
+			$url = $this->Url->build($url);
 		} else {
-			$url = $this->url($title);
+			$url = $this->Url->build($title);
 			$title = htmlspecialchars_decode($url, ENT_QUOTES);
 			$title = h(urldecode($title));
 			$escapeTitle = false;
@@ -400,7 +407,7 @@ class HtmlHelper extends Helper {
 		if (strpos($path, '//') !== false) {
 			$url = $path;
 		} else {
-			$url = $this->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.cssBaseUrl'), 'ext' => '.css'));
+			$url = $this->Url->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.cssBaseUrl'), 'ext' => '.css'));
 			$options = array_diff_key($options, array('fullBase' => null, 'pathPrefix' => null));
 		}
 
@@ -483,7 +490,7 @@ class HtmlHelper extends Helper {
 		}
 
 		if (strpos($url, '//') === false) {
-			$url = $this->assetUrl($url, $options + array('pathPrefix' => Configure::read('App.jsBaseUrl'), 'ext' => '.js'));
+			$url = $this->Url->assetUrl($url, $options + array('pathPrefix' => Configure::read('App.jsBaseUrl'), 'ext' => '.js'));
 			$options = array_diff_key($options, array('fullBase' => null, 'pathPrefix' => null));
 		}
 
@@ -753,7 +760,7 @@ class HtmlHelper extends Helper {
  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::image
  */
 	public function image($path, array $options = array()) {
-		$path = $this->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.imageBaseUrl')));
+		$path = $this->Url->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.imageBaseUrl')));
 		$options = array_diff_key($options, array('fullBase' => null, 'pathPrefix' => null));
 
 		if (!isset($options['alt'])) {
@@ -773,7 +780,7 @@ class HtmlHelper extends Helper {
 
 		if ($url) {
 			return $this->formatTemplate('link', [
-				'url' => $this->url($url),
+				'url' => $this->Url->build($url),
 				'attrs' => null,
 				'content' => $image
 			]);
@@ -1034,7 +1041,7 @@ class HtmlHelper extends Helper {
 					$ext = pathinfo($source['src'], PATHINFO_EXTENSION);
 					$source['type'] = $this->response->getMimeType($ext);
 				}
-				$source['src'] = $this->assetUrl($source['src'], $options);
+				$source['src'] = $this->Url->assetUrl($source['src'], $options);
 				$sourceTags .= $this->formatTemplate('tagselfclosing', [
 					'tag' => 'source',
 					'attrs' => $this->templater()->formatAttributes($source)
@@ -1047,7 +1054,7 @@ class HtmlHelper extends Helper {
 			if (empty($path) && !empty($options['src'])) {
 				$path = $options['src'];
 			}
-			$options['src'] = $this->assetUrl($path, $options);
+			$options['src'] = $this->Url->assetUrl($path, $options);
 		}
 
 		if ($tag === null) {
@@ -1064,7 +1071,7 @@ class HtmlHelper extends Helper {
 		}
 
 		if (isset($options['poster'])) {
-			$options['poster'] = $this->assetUrl($options['poster'], array('pathPrefix' => Configure::read('App.imageBaseUrl')) + $options);
+			$options['poster'] = $this->Url->assetUrl($options['poster'], array('pathPrefix' => Configure::read('App.imageBaseUrl')) + $options);
 		}
 		$text = $options['text'];
 

+ 8 - 1
src/View/Helper/PaginatorHelper.php

@@ -30,6 +30,13 @@ class PaginatorHelper extends Helper {
 	use StringTemplateTrait;
 
 /**
+ * List of helpers used by this helper
+ *
+ * @var array
+ */
+	public $helpers = ['Url'];
+
+/**
  * Defualt config for this class
  *
  * Options: Holds the default options for pagination links
@@ -426,7 +433,7 @@ class PaginatorHelper extends Helper {
 		) {
 			$url['sort'] = $url['direction'] = null;
 		}
-		return $this->url($url, $full);
+		return $this->Url->build($url, $full);
 	}
 
 /**

+ 6 - 6
src/View/Helper/RssHelper.php

@@ -30,7 +30,7 @@ class RssHelper extends Helper {
  *
  * @var array
  */
-	public $helpers = array('Time');
+	public $helpers = ['Url', 'Time'];
 
 /**
  * Base URL
@@ -127,7 +127,7 @@ class RssHelper extends Helper {
 		if (!isset($elements['description'])) {
 			$elements['description'] = '';
 		}
-		$elements['link'] = $this->url($elements['link'], true);
+		$elements['link'] = $this->Url->build($elements['link'], true);
 
 		$elems = '';
 		foreach ($elements as $elem => $data) {
@@ -228,14 +228,14 @@ class RssHelper extends Helper {
 						unset($attrib['url']);
 						$val = $val['url'];
 					}
-					$val = $this->url($val, true);
+					$val = $this->Url->build($val, true);
 					break;
 				case 'source':
 					if (is_array($val) && isset($val['url'])) {
-						$attrib['url'] = $this->url($val['url'], true);
+						$attrib['url'] = $this->Url->build($val['url'], true);
 						$val = $val['title'];
 					} elseif (is_array($val)) {
-						$attrib['url'] = $this->url($val[0], true);
+						$attrib['url'] = $this->Url->build($val[0], true);
 						$val = $val[1];
 					}
 					break;
@@ -248,7 +248,7 @@ class RssHelper extends Helper {
 							$val['type'] = mime_content_type(WWW_ROOT . $val['url']);
 						}
 					}
-					$val['url'] = $this->url($val['url'], true);
+					$val['url'] = $this->Url->build($val['url'], true);
 					$attrib = $val;
 					$val = null;
 					break;

+ 183 - 0
src/View/Helper/UrlHelper.php

@@ -0,0 +1,183 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.0.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\View\Helper;
+
+use Cake\Core\Configure;
+use Cake\Core\Plugin;
+use Cake\Routing\Router;
+use Cake\Utility\Inflector;
+use Cake\View\Helper;
+
+/**
+ * UrlHelper class for generating urls.
+ */
+class UrlHelper extends Helper {
+
+/**
+ * Returns a URL based on provided provided parameters.
+ *
+ * @param string|array $url Either a relative string url like `/products/view/23` or
+ *    an array of URL parameters. Using an array for URLs will allow you to leverage
+ *    the reverse routing features of CakePHP.
+ * @param bool $full If true, the full base URL will be prepended to the result
+ * @return string Full translated URL with base path.
+ * @link http://book.cakephp.org/2.0/en/views/helpers.html
+ */
+	public function build($url = null, $full = false) {
+		return h(Router::url($url, $full));
+	}
+
+/**
+ * Generate URL for given asset file. Depending on options passed provides full URL with domain name.
+ * Also calls Helper::assetTimestamp() to add timestamp to local files
+ *
+ * @param string|array $path Path string or URL array
+ * @param array $options Options array. Possible keys:
+ *   `fullBase` Return full URL with domain name
+ *   `pathPrefix` Path prefix for relative URLs
+ *   `ext` Asset extension to append
+ *   `plugin` False value will prevent parsing path as a plugin
+ * @return string Generated URL
+ */
+	public function assetUrl($path, array $options = array()) {
+		if (is_array($path)) {
+			return $this->build($path, !empty($options['fullBase']));
+		}
+		if (strpos($path, '://') !== false) {
+			return $path;
+		}
+		if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
+			list($plugin, $path) = $this->_View->pluginSplit($path, false);
+		}
+		if (!empty($options['pathPrefix']) && $path[0] !== '/') {
+			$path = $options['pathPrefix'] . $path;
+		}
+		if (
+			!empty($options['ext']) &&
+			strpos($path, '?') === false &&
+			substr($path, -strlen($options['ext'])) !== $options['ext']
+		) {
+			$path .= $options['ext'];
+		}
+		if (preg_match('|^([a-z0-9]+:)?//|', $path)) {
+			return $path;
+		}
+		if (isset($plugin)) {
+			$path = Inflector::underscore($plugin) . '/' . $path;
+		}
+		$path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path)));
+
+		if (!empty($options['fullBase'])) {
+			$path = rtrim(Router::fullBaseUrl(), '/') . '/' . ltrim($path, '/');
+		}
+		return $path;
+	}
+
+/**
+ * Encodes a URL for use in HTML attributes.
+ *
+ * @param string $url The URL to encode.
+ * @return string The URL encoded for both URL & HTML contexts.
+ */
+	protected function _encodeUrl($url) {
+		$path = parse_url($url, PHP_URL_PATH);
+		$parts = array_map('rawurldecode', explode('/', $path));
+		$parts = array_map('rawurlencode', $parts);
+		$encoded = implode('/', $parts);
+		return h(str_replace($path, $encoded, $url));
+	}
+
+/**
+ * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
+ * Configure. If Asset.timestamp is true and debug is true, or Asset.timestamp === 'force'
+ * a timestamp will be added.
+ *
+ * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
+ * @return string Path with a timestamp added, or not.
+ */
+	public function assetTimestamp($path) {
+		$stamp = Configure::read('Asset.timestamp');
+		$timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug'));
+		if ($timestampEnabled && strpos($path, '?') === false) {
+			$filepath = preg_replace(
+				'/^' . preg_quote($this->request->webroot, '/') . '/',
+				'',
+				urldecode($path)
+			);
+			$webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
+			if (file_exists($webrootPath)) {
+				//@codingStandardsIgnoreStart
+				return $path . '?' . @filemtime($webrootPath);
+				//@codingStandardsIgnoreEnd
+			}
+			$segments = explode('/', ltrim($filepath, '/'));
+			$plugin = Inflector::camelize($segments[0]);
+			if (Plugin::loaded($plugin)) {
+				unset($segments[0]);
+				$pluginPath = Plugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
+				//@codingStandardsIgnoreStart
+				return $path . '?' . @filemtime($pluginPath);
+				//@codingStandardsIgnoreEnd
+			}
+		}
+		return $path;
+	}
+
+/**
+ * Checks if a file exists when theme is used, if no file is found default location is returned
+ *
+ * @param string $file The file to create a webroot path to.
+ * @return string Web accessible path to file.
+ */
+	public function webroot($file) {
+		$asset = explode('?', $file);
+		$asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
+		$webPath = $this->request->webroot . $asset[0];
+		$file = $asset[0];
+
+		if (!empty($this->theme)) {
+			$file = trim($file, '/');
+			$theme = Inflector::underscore($this->theme) . '/';
+
+			if (DS === '\\') {
+				$file = str_replace('/', '\\', $file);
+			}
+
+			if (file_exists(Configure::read('App.www_root') . $theme . $file)) {
+				$webPath = $this->request->webroot . $theme . $asset[0];
+			} else {
+				$themePath = Plugin::path($this->theme);
+				$path = $themePath . 'webroot/' . $file;
+				if (file_exists($path)) {
+					$webPath = $this->request->webroot . $theme . $asset[0];
+				}
+			}
+		}
+		if (strpos($webPath, '//') !== false) {
+			return str_replace('//', '/', $webPath . $asset[1]);
+		}
+		return $webPath . $asset[1];
+	}
+
+/**
+ * Event listeners.
+ *
+ * @return array
+ */
+	public function implementedEvents() {
+		return [];
+	}
+
+}

+ 7 - 6
tests/TestCase/View/Helper/FormHelperTest.php

@@ -142,12 +142,13 @@ class FormHelperTest extends TestCase {
 		$this->View = new View();
 
 		$this->Form = new FormHelper($this->View);
-		$this->Form->request = new Request('articles/add');
-		$this->Form->request->here = '/articles/add';
-		$this->Form->request['controller'] = 'articles';
-		$this->Form->request['action'] = 'add';
-		$this->Form->request->webroot = '';
-		$this->Form->request->base = '';
+		$request = new Request('articles/add');
+		$request->here = '/articles/add';
+		$request['controller'] = 'articles';
+		$request['action'] = 'add';
+		$request->webroot = '';
+		$request->base = '';
+		$this->Form->Url->request = $this->Form->request = $request;
 
 		$this->dateRegex = array(
 			'daysRegex' => 'preg:/(?:<option value="0?([\d]+)">\\1<\/option>[\r\n]*)*/',

+ 12 - 11
tests/TestCase/View/Helper/HtmlHelperTest.php

@@ -68,6 +68,7 @@ class HtmlHelperTest extends TestCase {
 		$this->Html = new HtmlHelper($this->View);
 		$this->Html->request = new Request();
 		$this->Html->request->webroot = '';
+		$this->Html->Url->request = $this->Html->request;
 
 		Configure::write('App.namespace', 'TestApp');
 		Plugin::load(['TestTheme']);
@@ -390,12 +391,12 @@ class HtmlHelperTest extends TestCase {
  */
 	public function testImageWithFullBase() {
 		$result = $this->Html->image('test.gif', array('fullBase' => true));
-		$here = $this->Html->url('/', true);
+		$here = $this->Html->Url->build('/', true);
 		$expected = array('img' => array('src' => $here . 'img/test.gif', 'alt' => ''));
 		$this->assertHtml($expected, $result);
 
 		$result = $this->Html->image('sub/test.gif', array('fullBase' => true));
-		$here = $this->Html->url('/', true);
+		$here = $this->Html->Url->build('/', true);
 		$expected = array('img' => array('src' => $here . 'img/sub/test.gif', 'alt' => ''));
 		$this->assertHtml($expected, $result);
 
@@ -405,7 +406,7 @@ class HtmlHelperTest extends TestCase {
 		Router::pushRequest($request);
 
 		$result = $this->Html->image('sub/test.gif', array('fullBase' => true));
-		$here = $this->Html->url('/', true);
+		$here = $this->Html->Url->build('/', true);
 		$expected = array('img' => array('src' => $here . 'img/sub/test.gif', 'alt' => ''));
 		$this->assertHtml($expected, $result);
 	}
@@ -452,8 +453,8 @@ class HtmlHelperTest extends TestCase {
 		Configure::write('Asset.timestamp', true);
 		Configure::write('debug', true);
 
-		$this->Html->request->webroot = '/';
-		$this->Html->theme = 'TestTheme';
+		$this->Html->Url->request->webroot = '/';
+		$this->Html->Url->theme = 'TestTheme';
 		$result = $this->Html->image('__cake_test_image.gif');
 		$expected = array(
 			'img' => array(
@@ -462,7 +463,7 @@ class HtmlHelperTest extends TestCase {
 		));
 		$this->assertHtml($expected, $result);
 
-		$this->Html->request->webroot = '/testing/';
+		$this->Html->Url->request->webroot = '/testing/';
 		$result = $this->Html->image('__cake_test_image.gif');
 		$expected = array(
 			'img' => array(
@@ -481,7 +482,7 @@ class HtmlHelperTest extends TestCase {
 		$webRoot = Configure::read('App.www_root');
 		Configure::write('App.www_root', TEST_APP . 'webroot/');
 
-		$this->Html->theme = 'TestTheme';
+		$this->Html->Url->theme = 'TestTheme';
 		$result = $this->Html->css('webroot_test');
 		$expected = array(
 			'link' => array('rel' => 'stylesheet', 'href' => 'preg:/.*test_theme\/css\/webroot_test\.css/')
@@ -618,7 +619,7 @@ class HtmlHelperTest extends TestCase {
  */
 	public function testCssWithFullBase() {
 		Configure::write('Asset.filter.css', false);
-		$here = $this->Html->url('/', true);
+		$here = $this->Html->Url->build('/', true);
 
 		$result = $this->Html->css('screen', array('fullBase' => true));
 		$expected = array(
@@ -993,7 +994,7 @@ class HtmlHelperTest extends TestCase {
  * @return void
  */
 	public function testScriptWithFullBase() {
-		$here = $this->Html->url('/', true);
+		$here = $this->Html->Url->build('/', true);
 
 		$result = $this->Html->script('foo', array('fullBase' => true));
 		$expected = array(
@@ -1022,8 +1023,8 @@ class HtmlHelperTest extends TestCase {
 		$testfile = WWW_ROOT . '/test_theme/js/__test_js.js';
 		new File($testfile, true);
 
-		$this->Html->request->webroot = '/';
-		$this->Html->theme = 'TestTheme';
+		$this->Html->Url->request->webroot = '/';
+		$this->Html->Url->theme = 'TestTheme';
 		$result = $this->Html->script('__test_js.js');
 		$expected = array(
 			'script' => array('src' => '/test_theme/js/__test_js.js')

+ 3 - 3
tests/TestCase/View/Helper/RssHelperTest.php

@@ -104,7 +104,7 @@ class RssHelperTest extends TestCase {
 			'Title',
 			'/title',
 			'<link',
-			$this->Rss->url('/', true),
+			$this->Rss->Url->build('/', true),
 			'/link',
 			'<description',
 			'content',
@@ -551,7 +551,7 @@ class RssHelperTest extends TestCase {
 			'<description',
 			'<![CDATA[descriptive words]]',
 			'/description',
-			'enclosure' => array('url' => $this->Rss->url('/test.flv', true)),
+			'enclosure' => array('url' => $this->Rss->Url->build('/test.flv', true)),
 			'<pubDate',
 			date('r', strtotime('2008-05-31 12:00:00')),
 			'/pubDate',
@@ -633,7 +633,7 @@ class RssHelperTest extends TestCase {
 			'<![CDATA[descriptive words]]',
 			'/description',
 			'enclosure' => array(
-				'url' => $this->Rss->url('/tests/cakephp.file.test.tmp', true),
+				'url' => $this->Rss->Url->build('/tests/cakephp.file.test.tmp', true),
 				'length' => filesize($tmpFile),
 				'type' => $type
 			),

+ 286 - 0
tests/TestCase/View/Helper/UrlHelperTest.php

@@ -0,0 +1,286 @@
+<?php
+/**
+ * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
+ * @since         3.0.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\View;
+
+use Cake\Core\Configure;
+use Cake\Core\Plugin;
+use Cake\Network\Request;
+use Cake\Routing\Router;
+use Cake\TestSuite\TestCase;
+use Cake\View\Helper\UrlHelper;
+use Cake\View\View;
+
+/**
+ * UrlHelperTest class
+ *
+ */
+class UrlHelperTest extends TestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ */
+	public function setUp() {
+		parent::setUp();
+
+		Router::reload();
+		$this->View = new View();
+		$this->Helper = new UrlHelper($this->View);
+		$this->Helper->request = new Request();
+
+		Configure::write('App.namespace', 'TestApp');
+		Plugin::load(['TestTheme']);
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ */
+	public function tearDown() {
+		parent::tearDown();
+		Configure::delete('Asset');
+
+		Plugin::unload();
+		unset($this->Helper, $this->View);
+	}
+
+/**
+ * Ensure HTML escaping of URL params. So link addresses are valid and not exploited
+ *
+ * @return void
+ */
+	public function testUrlConversion() {
+		Router::connect('/:controller/:action/*');
+
+		$result = $this->Helper->build('/controller/action/1');
+		$this->assertEquals('/controller/action/1', $result);
+
+		$result = $this->Helper->build('/controller/action/1?one=1&two=2');
+		$this->assertEquals('/controller/action/1?one=1&amp;two=2', $result);
+
+		$result = $this->Helper->build(array('controller' => 'posts', 'action' => 'index', 'page' => '1" onclick="alert(\'XSS\');"'));
+		$this->assertEquals("/posts/index?page=1%22+onclick%3D%22alert%28%27XSS%27%29%3B%22", $result);
+
+		$result = $this->Helper->build('/controller/action/1/param:this+one+more');
+		$this->assertEquals('/controller/action/1/param:this+one+more', $result);
+
+		$result = $this->Helper->build('/controller/action/1/param:this%20one%20more');
+		$this->assertEquals('/controller/action/1/param:this%20one%20more', $result);
+
+		$result = $this->Helper->build('/controller/action/1/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24');
+		$this->assertEquals('/controller/action/1/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24', $result);
+
+		$result = $this->Helper->build(array(
+			'controller' => 'posts', 'action' => 'index', 'param' => '%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24'
+		));
+		$this->assertEquals("/posts/index?param=%257Baround%2520here%257D%255Bthings%255D%255Bare%255D%2524%2524", $result);
+
+		$result = $this->Helper->build(array(
+			'controller' => 'posts', 'action' => 'index', 'page' => '1',
+			'?' => array('one' => 'value', 'two' => 'value', 'three' => 'purple')
+		));
+		$this->assertEquals("/posts/index?page=1&amp;one=value&amp;two=value&amp;three=purple", $result);
+	}
+
+/**
+ * test assetTimestamp application
+ *
+ * @return void
+ */
+	public function testAssetTimestamp() {
+		Configure::write('Foo.bar', 'test');
+		Configure::write('Asset.timestamp', false);
+		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
+		$this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css', $result);
+
+		Configure::write('Asset.timestamp', true);
+		Configure::write('debug', false);
+
+		$result = $this->Helper->assetTimestamp('/%3Cb%3E/cake.generic.css');
+		$this->assertEquals('/%3Cb%3E/cake.generic.css', $result);
+
+		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
+		$this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css', $result);
+
+		Configure::write('Asset.timestamp', true);
+		Configure::write('debug', true);
+		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
+		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+
+		Configure::write('Asset.timestamp', 'force');
+		Configure::write('debug', false);
+		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
+		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+
+		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css?someparam');
+		$this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css?someparam', $result);
+
+		$this->Helper->request->webroot = '/some/dir/';
+		$result = $this->Helper->assetTimestamp('/some/dir/' . Configure::read('App.cssBaseUrl') . 'cake.generic.css');
+		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+	}
+
+/**
+ * test assetUrl application
+ *
+ * @return void
+ */
+	public function testAssetUrl() {
+		Router::connect('/:controller/:action/*');
+
+		$this->Helper->webroot = '';
+		$result = $this->Helper->assetUrl(array(
+				'controller' => 'js',
+				'action' => 'post',
+				'_ext' => 'js'
+			),
+			array('fullBase' => true)
+		);
+		$this->assertEquals(Router::fullBaseUrl() . '/js/post.js', $result);
+
+		$result = $this->Helper->assetUrl('foo.jpg', array('pathPrefix' => 'img/'));
+		$this->assertEquals('img/foo.jpg', $result);
+
+		$result = $this->Helper->assetUrl('foo.jpg', array('fullBase' => true));
+		$this->assertEquals(Router::fullBaseUrl() . '/foo.jpg', $result);
+
+		$result = $this->Helper->assetUrl('style', array('ext' => '.css'));
+		$this->assertEquals('style.css', $result);
+
+		$result = $this->Helper->assetUrl('dir/sub dir/my image', array('ext' => '.jpg'));
+		$this->assertEquals('dir/sub%20dir/my%20image.jpg', $result);
+
+		$result = $this->Helper->assetUrl('foo.jpg?one=two&three=four');
+		$this->assertEquals('foo.jpg?one=two&amp;three=four', $result);
+
+		$result = $this->Helper->assetUrl('dir/big+tall/image', array('ext' => '.jpg'));
+		$this->assertEquals('dir/big%2Btall/image.jpg', $result);
+	}
+
+/**
+ * Test assetUrl with no rewriting.
+ *
+ * @return void
+ */
+	public function testAssetUrlNoRewrite() {
+		$this->Helper->request->addPaths(array(
+			'base' => '/cake_dev/index.php',
+			'webroot' => '/cake_dev/app/webroot/',
+			'here' => '/cake_dev/index.php/tasks',
+		));
+		$result = $this->Helper->assetUrl('img/cake.icon.png', array('fullBase' => true));
+		$expected = Configure::read('App.fullBaseUrl') . '/cake_dev/app/webroot/img/cake.icon.png';
+		$this->assertEquals($expected, $result);
+	}
+
+/**
+ * Test assetUrl with plugins.
+ *
+ * @return void
+ */
+	public function testAssetUrlPlugin() {
+		$this->Helper->webroot = '';
+		Plugin::load('TestPlugin');
+
+		$result = $this->Helper->assetUrl('TestPlugin.style', array('ext' => '.css'));
+		$this->assertEquals('test_plugin/style.css', $result);
+
+		$result = $this->Helper->assetUrl('TestPlugin.style', array('ext' => '.css', 'plugin' => false));
+		$this->assertEquals('TestPlugin.style.css', $result);
+
+		Plugin::unload('TestPlugin');
+	}
+
+/**
+ * test assetUrl and Asset.timestamp = force
+ *
+ * @return void
+ */
+	public function testAssetUrlTimestampForce() {
+		$this->Helper->webroot = '';
+		Configure::write('Asset.timestamp', 'force');
+
+		$result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => Configure::read('App.cssBaseUrl')));
+		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+	}
+
+/**
+ * test assetTimestamp with plugins and themes
+ *
+ * @return void
+ */
+	public function testAssetTimestampPluginsAndThemes() {
+		Configure::write('Asset.timestamp', 'force');
+		Plugin::load(array('TestPlugin'));
+
+		$result = $this->Helper->assetTimestamp('/test_plugin/css/test_plugin_asset.css');
+		$this->assertRegExp('#/test_plugin/css/test_plugin_asset.css\?[0-9]+$#', $result, 'Missing timestamp plugin');
+
+		$result = $this->Helper->assetTimestamp('/test_plugin/css/i_dont_exist.css');
+		$this->assertRegExp('#/test_plugin/css/i_dont_exist.css\?$#', $result, 'No error on missing file');
+
+		$result = $this->Helper->assetTimestamp('/test_theme/js/theme.js');
+		$this->assertRegExp('#/test_theme/js/theme.js\?[0-9]+$#', $result, 'Missing timestamp theme');
+
+		$result = $this->Helper->assetTimestamp('/test_theme/js/non_existant.js');
+		$this->assertRegExp('#/test_theme/js/non_existant.js\?$#', $result, 'No error on missing file');
+	}
+
+/**
+ * Test generating paths with webroot().
+ *
+ * @return void
+ */
+	public function testWebrootPaths() {
+		$this->Helper->request->webroot = '/';
+		$result = $this->Helper->webroot('/img/cake.power.gif');
+		$expected = '/img/cake.power.gif';
+		$this->assertEquals($expected, $result);
+
+		$this->Helper->theme = 'TestTheme';
+
+		$result = $this->Helper->webroot('/img/cake.power.gif');
+		$expected = '/test_theme/img/cake.power.gif';
+		$this->assertEquals($expected, $result);
+
+		$result = $this->Helper->webroot('/img/test.jpg');
+		$expected = '/test_theme/img/test.jpg';
+		$this->assertEquals($expected, $result);
+
+		$webRoot = Configure::read('App.www_root');
+		Configure::write('App.www_root', TEST_APP . 'TestApp/webroot/');
+
+		$result = $this->Helper->webroot('/img/cake.power.gif');
+		$expected = '/test_theme/img/cake.power.gif';
+		$this->assertEquals($expected, $result);
+
+		$result = $this->Helper->webroot('/img/test.jpg');
+		$expected = '/test_theme/img/test.jpg';
+		$this->assertEquals($expected, $result);
+
+		$result = $this->Helper->webroot('/img/cake.icon.gif');
+		$expected = '/img/cake.icon.gif';
+		$this->assertEquals($expected, $result);
+
+		$result = $this->Helper->webroot('/img/cake.icon.gif?some=param');
+		$expected = '/img/cake.icon.gif?some=param';
+		$this->assertEquals($expected, $result);
+
+		Configure::write('App.www_root', $webRoot);
+	}
+
+}

+ 0 - 332
tests/TestCase/View/HelperTest.php

@@ -16,118 +16,14 @@
  */
 namespace Cake\Test\TestCase\View;
 
-use Cake\Core\App;
 use Cake\Core\Configure;
 use Cake\Core\Plugin;
 use Cake\Network\Request;
-use Cake\ORM\Table;
 use Cake\Routing\Router;
 use Cake\TestSuite\TestCase;
 use Cake\View\Helper;
 use Cake\View\View;
 
-/**
- * HelperTestPost class
- *
- */
-class HelperTestPostsTable extends Table {
-
-/**
- * schema method
- *
- * @return void
- */
-	public function schema($field = false) {
-		$this->_schema = array(
-			'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-			'title' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
-			'body' => array('type' => 'string', 'null' => true, 'default' => '', 'length' => ''),
-			'number' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-			'date' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
-			'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
-			'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
-		);
-		return $this->_schema;
-	}
-
-/**
- * hasAndBelongsToMany property
- *
- * @var array
- */
-	public $hasAndBelongsToMany = array('HelperTestTag' => array('with' => 'HelperTestPostsTag'));
-}
-
-/**
- * HelperTestComment class
- *
- */
-class HelperTestCommentsTable extends Table {
-
-/**
- * schema method
- *
- * @return void
- */
-	public function schema($field = false) {
-		$this->_schema = array(
-			'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-			'author_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-			'title' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
-			'body' => array('type' => 'string', 'null' => true, 'default' => '', 'length' => ''),
-			'BigField' => array('type' => 'string', 'null' => true, 'default' => '', 'length' => ''),
-			'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
-			'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
-		);
-		return $this->_schema;
-	}
-
-}
-
-/**
- * HelperTestTag class
- *
- */
-class HelperTestTagsTable extends Table {
-
-/**
- * schema method
- *
- * @return void
- */
-	public function schema($field = false) {
-		$this->_schema = array(
-			'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-			'name' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
-			'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
-			'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
-		);
-		return $this->_schema;
-	}
-
-}
-
-/**
- * HelperTestPostsTag class
- *
- */
-class HelperTestPostsTagsTable extends Table {
-
-/**
- * schema method
- *
- * @return void
- */
-	public function schema($field = false) {
-		$this->_schema = array(
-			'helper_test_post_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-			'helper_test_tag_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
-		);
-		return $this->_schema;
-	}
-
-}
-
 class TestHelper extends Helper {
 
 /**
@@ -180,9 +76,6 @@ class HelperTest extends TestCase {
 		$this->View = new View();
 		$this->Helper = new Helper($this->View);
 		$this->Helper->request = new Request();
-
-		Configure::write('App.namespace', 'TestApp');
-		Plugin::load(['TestTheme']);
 	}
 
 /**
@@ -217,231 +110,6 @@ class HelperTest extends TestCase {
 	}
 
 /**
- * Ensure HTML escaping of URL params. So link addresses are valid and not exploited
- *
- * @return void
- */
-	public function testUrlConversion() {
-		Router::connect('/:controller/:action/*');
-
-		$result = $this->Helper->url('/controller/action/1');
-		$this->assertEquals('/controller/action/1', $result);
-
-		$result = $this->Helper->url('/controller/action/1?one=1&two=2');
-		$this->assertEquals('/controller/action/1?one=1&amp;two=2', $result);
-
-		$result = $this->Helper->url(array('controller' => 'posts', 'action' => 'index', 'page' => '1" onclick="alert(\'XSS\');"'));
-		$this->assertEquals("/posts/index?page=1%22+onclick%3D%22alert%28%27XSS%27%29%3B%22", $result);
-
-		$result = $this->Helper->url('/controller/action/1/param:this+one+more');
-		$this->assertEquals('/controller/action/1/param:this+one+more', $result);
-
-		$result = $this->Helper->url('/controller/action/1/param:this%20one%20more');
-		$this->assertEquals('/controller/action/1/param:this%20one%20more', $result);
-
-		$result = $this->Helper->url('/controller/action/1/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24');
-		$this->assertEquals('/controller/action/1/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24', $result);
-
-		$result = $this->Helper->url(array(
-			'controller' => 'posts', 'action' => 'index', 'param' => '%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24'
-		));
-		$this->assertEquals("/posts/index?param=%257Baround%2520here%257D%255Bthings%255D%255Bare%255D%2524%2524", $result);
-
-		$result = $this->Helper->url(array(
-			'controller' => 'posts', 'action' => 'index', 'page' => '1',
-			'?' => array('one' => 'value', 'two' => 'value', 'three' => 'purple')
-		));
-		$this->assertEquals("/posts/index?page=1&amp;one=value&amp;two=value&amp;three=purple", $result);
-	}
-
-/**
- * test assetTimestamp application
- *
- * @return void
- */
-	public function testAssetTimestamp() {
-		Configure::write('Foo.bar', 'test');
-		Configure::write('Asset.timestamp', false);
-		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
-		$this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css', $result);
-
-		Configure::write('Asset.timestamp', true);
-		Configure::write('debug', false);
-
-		$result = $this->Helper->assetTimestamp('/%3Cb%3E/cake.generic.css');
-		$this->assertEquals('/%3Cb%3E/cake.generic.css', $result);
-
-		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
-		$this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css', $result);
-
-		Configure::write('Asset.timestamp', true);
-		Configure::write('debug', true);
-		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
-		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
-
-		Configure::write('Asset.timestamp', 'force');
-		Configure::write('debug', false);
-		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css');
-		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
-
-		$result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css?someparam');
-		$this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css?someparam', $result);
-
-		$this->Helper->request->webroot = '/some/dir/';
-		$result = $this->Helper->assetTimestamp('/some/dir/' . Configure::read('App.cssBaseUrl') . 'cake.generic.css');
-		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
-	}
-
-/**
- * test assetUrl application
- *
- * @return void
- */
-	public function testAssetUrl() {
-		Router::connect('/:controller/:action/*');
-
-		$this->Helper->webroot = '';
-		$result = $this->Helper->assetUrl(array(
-				'controller' => 'js',
-				'action' => 'post',
-				'_ext' => 'js'
-			),
-			array('fullBase' => true)
-		);
-		$this->assertEquals(Router::fullBaseUrl() . '/js/post.js', $result);
-
-		$result = $this->Helper->assetUrl('foo.jpg', array('pathPrefix' => 'img/'));
-		$this->assertEquals('img/foo.jpg', $result);
-
-		$result = $this->Helper->assetUrl('foo.jpg', array('fullBase' => true));
-		$this->assertEquals(Router::fullBaseUrl() . '/foo.jpg', $result);
-
-		$result = $this->Helper->assetUrl('style', array('ext' => '.css'));
-		$this->assertEquals('style.css', $result);
-
-		$result = $this->Helper->assetUrl('dir/sub dir/my image', array('ext' => '.jpg'));
-		$this->assertEquals('dir/sub%20dir/my%20image.jpg', $result);
-
-		$result = $this->Helper->assetUrl('foo.jpg?one=two&three=four');
-		$this->assertEquals('foo.jpg?one=two&amp;three=four', $result);
-
-		$result = $this->Helper->assetUrl('dir/big+tall/image', array('ext' => '.jpg'));
-		$this->assertEquals('dir/big%2Btall/image.jpg', $result);
-	}
-
-/**
- * Test assetUrl with no rewriting.
- *
- * @return void
- */
-	public function testAssetUrlNoRewrite() {
-		$this->Helper->request->addPaths(array(
-			'base' => '/cake_dev/index.php',
-			'webroot' => '/cake_dev/app/webroot/',
-			'here' => '/cake_dev/index.php/tasks',
-		));
-		$result = $this->Helper->assetUrl('img/cake.icon.png', array('fullBase' => true));
-		$expected = Configure::read('App.fullBaseUrl') . '/cake_dev/app/webroot/img/cake.icon.png';
-		$this->assertEquals($expected, $result);
-	}
-
-/**
- * Test assetUrl with plugins.
- *
- * @return void
- */
-	public function testAssetUrlPlugin() {
-		$this->Helper->webroot = '';
-		Plugin::load('TestPlugin');
-
-		$result = $this->Helper->assetUrl('TestPlugin.style', array('ext' => '.css'));
-		$this->assertEquals('test_plugin/style.css', $result);
-
-		$result = $this->Helper->assetUrl('TestPlugin.style', array('ext' => '.css', 'plugin' => false));
-		$this->assertEquals('TestPlugin.style.css', $result);
-
-		Plugin::unload('TestPlugin');
-	}
-
-/**
- * test assetUrl and Asset.timestamp = force
- *
- * @return void
- */
-	public function testAssetUrlTimestampForce() {
-		$this->Helper->webroot = '';
-		Configure::write('Asset.timestamp', 'force');
-
-		$result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => Configure::read('App.cssBaseUrl')));
-		$this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result);
-	}
-
-/**
- * test assetTimestamp with plugins and themes
- *
- * @return void
- */
-	public function testAssetTimestampPluginsAndThemes() {
-		Configure::write('Asset.timestamp', 'force');
-		Plugin::load(array('TestPlugin'));
-
-		$result = $this->Helper->assetTimestamp('/test_plugin/css/test_plugin_asset.css');
-		$this->assertRegExp('#/test_plugin/css/test_plugin_asset.css\?[0-9]+$#', $result, 'Missing timestamp plugin');
-
-		$result = $this->Helper->assetTimestamp('/test_plugin/css/i_dont_exist.css');
-		$this->assertRegExp('#/test_plugin/css/i_dont_exist.css\?$#', $result, 'No error on missing file');
-
-		$result = $this->Helper->assetTimestamp('/test_theme/js/theme.js');
-		$this->assertRegExp('#/test_theme/js/theme.js\?[0-9]+$#', $result, 'Missing timestamp theme');
-
-		$result = $this->Helper->assetTimestamp('/test_theme/js/non_existant.js');
-		$this->assertRegExp('#/test_theme/js/non_existant.js\?$#', $result, 'No error on missing file');
-	}
-
-/**
- * Test generating paths with webroot().
- *
- * @return void
- */
-	public function testWebrootPaths() {
-		$this->Helper->request->webroot = '/';
-		$result = $this->Helper->webroot('/img/cake.power.gif');
-		$expected = '/img/cake.power.gif';
-		$this->assertEquals($expected, $result);
-
-		$this->Helper->theme = 'TestTheme';
-
-		$result = $this->Helper->webroot('/img/cake.power.gif');
-		$expected = '/test_theme/img/cake.power.gif';
-		$this->assertEquals($expected, $result);
-
-		$result = $this->Helper->webroot('/img/test.jpg');
-		$expected = '/test_theme/img/test.jpg';
-		$this->assertEquals($expected, $result);
-
-		$webRoot = Configure::read('App.www_root');
-		Configure::write('App.www_root', TEST_APP . 'TestApp/webroot/');
-
-		$result = $this->Helper->webroot('/img/cake.power.gif');
-		$expected = '/test_theme/img/cake.power.gif';
-		$this->assertEquals($expected, $result);
-
-		$result = $this->Helper->webroot('/img/test.jpg');
-		$expected = '/test_theme/img/test.jpg';
-		$this->assertEquals($expected, $result);
-
-		$result = $this->Helper->webroot('/img/cake.icon.gif');
-		$expected = '/img/cake.icon.gif';
-		$this->assertEquals($expected, $result);
-
-		$result = $this->Helper->webroot('/img/cake.icon.gif?some=param');
-		$expected = '/img/cake.icon.gif?some=param';
-		$this->assertEquals($expected, $result);
-
-		Configure::write('App.www_root', $webRoot);
-	}
-
-/**
  * test lazy loading helpers is seamless
  *
  * @return void