diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b0672a6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+vendor/*
+tests/test_files/webroot/css/*
diff --git a/composer.json b/composer.json
index 03381e4..e4fc1cc 100644
--- a/composer.json
+++ b/composer.json
@@ -14,7 +14,6 @@
"license": "Apache-2.0",
"require": {
"php": ">=5.4.19",
- "cakephp/plugin-installer": "*",
"cakephp/cakephp": "3.0.x-dev",
"oyejorge/less.php": "1.7.0.2"
},
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..973fab8
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,11 @@
+
+
+
+
+
+ ./tests/
+
+
+
diff --git a/src/View/Helper/LessHelper.php b/src/View/Helper/LessHelper.php
index 2325e41..21a655e 100644
--- a/src/View/Helper/LessHelper.php
+++ b/src/View/Helper/LessHelper.php
@@ -8,6 +8,7 @@
namespace Less\View\Helper;
use Cake\Core\Plugin;
+use Cake\Core\Configure;
use Cake\Log\Log;
use Cake\View\View;
use Cake\Routing\Router;
@@ -15,6 +16,9 @@
class LessHelper extends Helper
{
+/**
+ * {@inheritdoc}
+ */
public $helpers = [
'Html', 'Url'
];
@@ -54,28 +58,32 @@ class LessHelper extends Helper
/**
* Initializes Lessc and cleans less and css paths
+ *
+ * {@inheritdoc}
*/
public function __construct(View $View, array $config = [])
{
parent::__construct($View, $config);
// Initialize oyejorge/less.php parser
- require ROOT . DS . 'vendor' . DS . 'oyejorge' . DS . 'less.php' . DS . 'lib' . DS . 'Less' . DS . 'Autoloader.php';
+ require_once ROOT . DS . 'vendor' . DS . 'oyejorge' . DS . 'less.php' . DS . 'lib' . DS . 'Less' . DS . 'Autoloader.php';
\Less_Autoloader::register();
- $this->css_path = trim($this->css_path, '/');
+ $this->css_path = WWW_ROOT . trim($this->css_path, '/');
}
/**
- * Compile the less and return a css tag.
- * In case of error, it will load less with javascript
- * instead of returning the resulting css tag.
+ * Compiles any less files passed and returns the compiled css.
+ * In case of error, it will load less with the javascritp parser so you'll be
+ * able to see any errors on screen. If not, check out the error.log file in your
+ * CakePHP's logs folder.
*
- * @param mixed $less The input .less file to be compiled or an array of .less files
- * @param array $parser_options The options to be passed to the less php compiler
- * @param array $lessjs_options An array of options to be passed as a json to the less javascript object.
- * @return string The resulting tag for the compiled css, or the tag for the .less & less.min if compilation fails
- * @throws Exception
+ * @param mixed $less The input .less file to be compiled or an array
+ * of .less files
+ * @param array $options Options in 'js' key will be pased to the less.js
+ * parser and options in 'parser' will be passed to the less.php parser
+ * @param array $modify_vars Array of modify vars
+ * @return string
*/
public function less($less = 'styles.less', array $options = [], array $modify_vars = [])
{
@@ -96,17 +104,21 @@ public function less($less = 'styles.less', array $options = [], array $modify_v
}
return $this->Html->css($css);
}
- catch (Exception $e) {
- $this->error = $e->getMessage();
+ catch (\Exception $e) {
+ // env must be development in order to see errors on-screen
+ if (Configure::read('debug')) {
+ $options['js']['env'] = 'development';
+ }
- Log::write('warning', "Error compiling less file: " . $this->error);
+ $this->error = $e->getMessage();
+ Log::write('error', "Error compiling less file: " . $this->error);
return $this->jsBlock($less, $options);
}
}
/**
- * Returns the initialization string for less (javascript based)
+ * Returns the required script and link tags to get less.js working
*
* @param string $less The input .less file to be loaded
* @param array $options An array of options to be passed to the `less` configuration var
@@ -132,16 +144,18 @@ public function jsBlock($less, array $options = [])
/**
* Compiles an input less file to an output css file using the PHP compiler
- *
- * @param string $input The input .less file to be compiled
- * @param string $output The output .css file, resulting from the compilation
- * @return boolean true on success, false otherwise
+ * @param array $input The input .less files to be compiled
+ * @param array $options Options to be passed to the php parser
+ * @param array $modify_vars Less modify_vars
+ * @param boolean $cache Whether to cache or not
+ * @return string If cache is not enabled will return the full CSS compiled.
+ * Otherwise it will return the resulting filename from the compilation.
*/
- public function compile($input, array $options = [], array $modify_vars = [], $cache = true)
+ public function compile(array $input, array $options = [], array $modify_vars = [], $cache = true)
{
$to_parse = [];
foreach ($input as $in) {
- $less = realpath($in);
+ $less = realpath(WWW_ROOT . $in);
// If we have plugin notation, ensure to properly load the files
list($plugin, $name) = $this->_View->pluginSplit($in, false);
if (!empty($plugin)) {
@@ -159,11 +173,14 @@ public function compile($input, array $options = [], array $modify_vars = [], $c
}
$lessc = new \Less_Parser($options);
- $lessc->ModifyVars($modify_vars);
foreach ($to_parse as $file => $path) {
$lessc->parseFile($file, $path);
}
+ // ModifyVars must be called at the bottom of the parsing,
+ // this way we're ensuring they override their default values.
+ // http://lesscss.org/usage/#command-line-usage-modify-variable
+ $lessc->ModifyVars($modify_vars);
return $lessc->getCss();
}
@@ -172,6 +189,9 @@ public function compile($input, array $options = [], array $modify_vars = [], $c
* Sets the less configuration var options based on the ones given by the user
* and our default ones.
*
+ * Here's also where we define the import_callback used by less.php parser,
+ * so it can find files successfully even if they're on plugin folders.
+ *
* @param array $options An array of options containing our options combined with the ones for the parsers
* @return array $options The resulting $options array
*/
@@ -226,17 +246,17 @@ private function setOptions(array $options)
return $options;
}
- /**
- * Returns tha full base url for the given asset
- *
- * @param string $asset The asset path
- * @param string $plugin Plugin where the asset resides
- * @return string
- */
- private function assetBaseUrl($asset, $plugin = 'Bootstrap')
+/**
+ * Returns tha full base url for the given asset
+ *
+ * @param string $asset The asset path
+ * @param string $plugin Plugin where the asset resides
+ * @return string
+ */
+ private function assetBaseUrl($asset, $plugin)
{
$dir = dirname($asset);
- $path = !empty($dir) ? "/$dir" : null;
+ $path = !empty($dir) && $dir != '.' ? "/$dir" : null;
return $this->Url->assetUrl($plugin . $path, [
'fullBase' => true
diff --git a/tests/TestCase/View/Helper/LessHelperTest.php b/tests/TestCase/View/Helper/LessHelperTest.php
new file mode 100644
index 0000000..9c9203f
--- /dev/null
+++ b/tests/TestCase/View/Helper/LessHelperTest.php
@@ -0,0 +1,119 @@
+Less = new LessHelper($View);
+ }
+
+ public function testLess()
+ {
+ $options = [
+ 'cache' => false
+ ];
+
+ // [Less.php] Basic compiling
+ $result = $this->Less->less('less/test.less', $options, ['bgcolor' => 'red']);
+ $this->assertHtml([
+ ['style' => true],
+ 'body{background-color: #f00}',
+ '/style'
+ ], $result);
+
+ // [Less.php] Compiling using cache (here we only check for the
+ // resulting tag, as the compilation checks are made in testCompile)
+ $result = $this->Less->less('less/test.less');
+ $this->assertHtml([
+ 'link' => [
+ 'rel' => 'stylesheet',
+ 'href' => 'preg:/\/css\/lessphp_[0-9a-z]+\.css/'
+ ]
+ ], $result);
+
+ // Trying the js fallback
+ $result = $this->Less->less('less/test_error.less');
+ $this->assertHtml([
+ 'link' => [
+ 'rel' => 'stylesheet/less',
+ 'href' => '/less/test_error.less'
+ ],
+ /*['script' => true],
+ // Need some help here :p
+ '/script',
+ 'script' => [
+ 'src' => '/less/js/less.min.js'
+ ]*/
+ ], $result);
+ }
+
+ // public function testJsBlock()
+ // {
+
+ // }
+
+ public function testCompile()
+ {
+ $options = [
+ 'compress' => true
+ ];
+
+ // Basic compiling
+ $result = $this->Less->compile(['less/test.less'], $options, [], false);
+ $this->assertTextEquals('body{background-color: #000}', $result);
+
+ // Changing the bgcolor var
+ $result = $this->Less->compile(['less/test.less'], $options, ['bgcolor' => 'magenta'], false);
+ $this->assertTextEquals('body{background-color: #f0f}', $result);
+
+ // Compiling with cache
+ $result = $this->Less->compile(['less/test.less'], $options);
+ $this->assertRegExp('/lessphp_[a-z0-9]+\.css/', $result);
+ $result = file_get_contents(WWW_ROOT . 'css' . DS . $result);
+ $this->assertTextEquals('body{background-color: #000}', $result);
+
+ // Compiling with cache and modify_vars
+ $result = $this->Less->compile(['less/test.less'], $options, ['bgcolor' => 'darkorange']);
+ $this->assertRegExp('/lessphp_[a-z0-9]+\.css/', $result);
+ $result = file_get_contents(WWW_ROOT . 'css' . DS . $result);
+ $this->assertTextEquals('body{background-color: #ff8c00}', $result);
+ }
+
+ public function testAssetBaseUrl()
+ {
+ $assetBaseUrl = self::getProtectedMethod('assetBaseUrl');
+ $result = $assetBaseUrl->invokeArgs($this->Less, [
+ 'less/styles.less',
+ 'Less'
+ ]);
+ $this->assertEquals('http://localhost/Less/less', $result);
+
+ $result = $assetBaseUrl->invokeArgs($this->Less, [
+ 'css/whatever.less',
+ 'Bootstrap'
+ ]);
+ $this->assertEquals('http://localhost/Bootstrap/css', $result);
+
+ $result = $assetBaseUrl->invokeArgs($this->Less, [
+ 'whatever.less',
+ 'Less'
+ ]);
+ $this->assertEquals('http://localhost/Less', $result);
+ }
+
+ protected static function getProtectedMethod($name) {
+ $class = new \ReflectionClass('Less\View\Helper\LessHelper');
+ $method = $class->getMethod($name);
+ $method->setAccessible(true);
+ return $method;
+ }
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..3642fbc
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,65 @@
+ 'App',
+ 'encoding' => 'UTF-8',
+ 'base' => false,
+ 'baseUrl' => false,
+ 'dir' => 'src',
+ 'webroot' => WEBROOT_DIR,
+ 'www_root' => WWW_ROOT,
+ 'fullBaseUrl' => 'http://localhost',
+ 'imageBaseUrl' => 'img/',
+ 'jsBaseUrl' => 'js/',
+ 'cssBaseUrl' => 'css/',
+ 'paths' => [
+ 'plugins' => [dirname(APP) . DS . 'plugins' . DS],
+ 'templates' => [APP . 'Template' . DS]
+ ]
+]);
+
+Cache::config([
+ '_cake_core_' => [
+ 'engine' => 'File',
+ 'prefix' => 'cake_core_',
+ 'serialize' => true
+ ],
+ '_cake_model_' => [
+ 'engine' => 'File',
+ 'prefix' => 'cake_model_',
+ 'serialize' => true
+ ]
+]);
+
+Plugin::load('Less', ['path' => ROOT]);
diff --git a/tests/test_files/webroot/css/.gitkeep b/tests/test_files/webroot/css/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_files/webroot/less/test.less b/tests/test_files/webroot/less/test.less
new file mode 100644
index 0000000..76b38b0
--- /dev/null
+++ b/tests/test_files/webroot/less/test.less
@@ -0,0 +1,5 @@
+@bgcolor: black;
+
+body {
+ background-color: @bgcolor;
+}
diff --git a/tests/test_files/webroot/less/test_error.less b/tests/test_files/webroot/less/test_error.less
new file mode 100644
index 0000000..1c3f741
--- /dev/null
+++ b/tests/test_files/webroot/less/test_error.less
@@ -0,0 +1,3 @@
+buggy {
+
+