├── .gitignore
├── CHANGELOG.md
├── .travis.yml
├── tests
├── bootstrap.php
├── phpunit.xml
└── src
│ └── AssetsInstallerTest.php
├── composer.json
├── LICENSE
├── phpunit.xml.dist
├── src
├── AssetsInstallerPlugin.php
├── DirectoryHandler.php
└── AssetsInstaller.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | vendor/
3 | composer.lock
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## v2.0.0
4 |
5 | - Handle [Symfony 3](http://symfony.com)
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.5
5 | - 5.6
6 | - 7.0
7 | - 7.1
8 |
9 | before_script: composer install
10 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | $loader = require __DIR__.'/../vendor/autoload.php';
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reputation-vip/composer-assets-installer",
3 | "license": "MIT",
4 | "type": "composer-plugin",
5 | "authors": [
6 | {
7 | "name": "Alban Pommeret",
8 | "email": "ap@reputationvip.com"
9 | }
10 | ],
11 | "require": {
12 | "composer-plugin-api": "~1.0",
13 | "symfony/filesystem": "^3.0"
14 | },
15 | "require-dev": {
16 | "composer/composer": "^1.4",
17 | "phpunit/phpunit": "~4.4"
18 | },
19 | "autoload": {
20 | "psr-4": {
21 | "ReputationVIP\\Composer\\": "src"
22 | }
23 | },
24 | "extra": {
25 | "class": "ReputationVIP\\Composer\\AssetsInstallerPlugin"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | .
17 |
18 |
19 |
20 |
21 |
22 | ../src/AssetsInstaller
23 |
24 | vendor
25 |
26 |
27 |
28 |
29 |
30 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Reputation VIP
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./tests
17 |
18 |
19 |
20 |
21 |
22 | ../src/AssetsInstaller
23 |
24 | vendor
25 |
26 |
27 |
28 |
29 |
30 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/AssetsInstallerPlugin.php:
--------------------------------------------------------------------------------
1 | assetsInstaller = new AssetsInstaller($composer, $io);
32 | }
33 |
34 | public static function getSubscribedEvents()
35 | {
36 | return array(
37 | ScriptEvents::POST_UPDATE_CMD => array(
38 | array('onPostInstall', 0)
39 | ),
40 | ScriptEvents::POST_INSTALL_CMD => array(
41 | array('onPostInstall', 0)
42 | )
43 | );
44 | }
45 |
46 | public function onPostInstall(Event $event)
47 | {
48 | $this->assetsInstaller->install();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/DirectoryHandler.php:
--------------------------------------------------------------------------------
1 | copyDirectory($srcFile, $dstFile);
29 | } else {
30 | copy($srcFile, $dstFile);
31 | }
32 | }
33 | }
34 | closedir($dir);
35 |
36 | return $this;
37 | }
38 |
39 | /**
40 | * @param string $dirPath
41 | * @return $this
42 | */
43 | public function deleteDirectory($dirPath)
44 | {
45 | if (!is_dir($dirPath)) {
46 | return false;
47 | }
48 | if (substr($dirPath, strlen($dirPath) - 1, 1) != DIRECTORY_SEPARATOR) {
49 | $dirPath .= DIRECTORY_SEPARATOR;
50 | }
51 | $files = glob($dirPath . '*', GLOB_MARK);
52 | foreach ($files as $file) {
53 | if (is_dir($file)) {
54 | $this->deleteDirectory($file);
55 | } else {
56 | unlink($file);
57 | }
58 | }
59 | rmdir($dirPath);
60 | return $this;
61 | }
62 |
63 | /**
64 | * @param string $path
65 | * @return bool
66 | */
67 | public function isDirectory($path)
68 | {
69 | return is_dir($path);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Composer Assets Installer [](https://travis-ci.org/ReputationVIP/composer-assets-installer)
2 |
3 | Composer Assets Installer provides a **fast and easy** way to **copy the assets of your Composer packages into your public folder**. You only have to chose one or multiple asset directories of the target in your "composer.json" file.
4 |
5 | ## Code Example
6 |
7 | Here is the composer.json file of a distant Composer package we want to use in our project :
8 |
9 | {
10 | "require": {
11 | "reputation-vip/composer-assets-installer": "~1.0"
12 | },
13 | "name": "reputation-vip/required-distant-package",
14 | "extra": {
15 | "assets-dir" : "public"
16 | }
17 | }
18 |
19 |
20 | Here is the composer.json file of our project :
21 |
22 | {
23 | "require": {
24 | "reputation-vip/composer-assets-installer": "~1.0",
25 | "my/required-distant-package": "~1.0"
26 | },
27 | "extra": {
28 | "assets-dir" : "web"
29 | }
30 | }
31 |
32 | Then, the assets will be accessible through the following path: **web/my/required-distant-package**.
33 |
34 | ## Motivation
35 |
36 | As members of the **Reputation VIP**'s development team, we are used to creating full Composer packages, embedding a javascript logic and a css layer.
37 |
38 | When we first started using Composer, we were somehow frustrated by it's lack of asset handling. Indeed, Composer forced us to manually copy the assets into our public directory. Furthermore, every time we updated the package, we had to repeat this task.
39 |
40 | That's why we needed a **tested, documented and easily configurable** Composer plugin which allowed us to **keep control on the asset directories**.
41 |
42 | ## Installation
43 |
44 | You simply have to add the following line to the requirements of your composer.json file:
45 |
46 | "require": {
47 | "reputation-vip/composer-assets-installer": "~1.0"
48 | }
49 |
50 | Then, you can specify the target for your asset directory (web for example):
51 |
52 | "extra": {
53 | "assets-dir": "web"
54 | }
55 |
56 |
57 | ## API Reference
58 |
59 | With this solution, you can specify as many targets as you want:
60 |
61 | "extra": {
62 | "assets-dir": {
63 | "js": "web/js",
64 | "css": "web/css"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/AssetsInstaller.php:
--------------------------------------------------------------------------------
1 |
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace ReputationVIP\Composer;
14 |
15 | use Composer\Composer;
16 | use Composer\Json\JsonFile;
17 | use Composer\IO\IOInterface;
18 | use Composer\Package;
19 | use Symfony\Component\Filesystem\Filesystem;
20 |
21 | class AssetsInstaller
22 | {
23 | const LABEL_ASSETS_DIR = "assets-dir";
24 | const LABEL_PUBLIC_DIR = "public";
25 |
26 | const LOG_WARNING = 1;
27 | const LOG_INFO = 2;
28 | const LOG_ERROR = 3;
29 |
30 | const CODE_INSTALLING_DIR = 1;
31 | const CODE_NO_MATCHING_DIR = 2;
32 | const CODE_MATCHING_DIR = 3;
33 | const CODE_NOT_DIRECTORY = 4;
34 | const CODE_DIRECTORY = 5;
35 | const CODE_DIR_INSTALLED = 6;
36 | const CODE_INSTALLING_PACKAGE = 7;
37 | const CODE_NO_INSTALL = 8;
38 |
39 | /**
40 | * @var Composer
41 | */
42 | public $composer;
43 | /**
44 | * @var IOInterface
45 | */
46 | private $io;
47 | /**
48 | * @var Package\RootPackageInterface
49 | */
50 | private $package;
51 | /**
52 | * @var string|null
53 | */
54 | private $vendorPath = null;
55 | /**
56 | * @var string
57 | */
58 | public $assetsDirectories = array(self::LABEL_PUBLIC_DIR => "public/assets/");
59 | /**
60 | * @var DirectoryHandler|null
61 | */
62 | private $directoryHandler;
63 |
64 | public $packagesStatuses = array();
65 |
66 |
67 | /**
68 | * Initializes the plugin
69 | * Reads the composer.json file and
70 | * retrieves the assets-dir set if any.
71 | * This assets-dir is the path where
72 | * the other packages assets will be installed
73 | *
74 | * @param Filesystem $fs
75 | * @param Composer $composer
76 | * @param IOInterface $io
77 | * @param null $directoryHandler
78 | */
79 | public function __construct($composer, $io, $directoryHandler = null, Filesystem $fs = null)
80 | {
81 | $this->composer = $composer;
82 | $this->io = $io;
83 | $this->directoryHandler = (!is_null($directoryHandler)) ? $directoryHandler : new DirectoryHandler();
84 | $this->fs = $fs ?: new Filesystem();
85 |
86 | // We get the current package instance
87 | $this->package = $composer->getPackage();
88 | // We read its extra section of composer.json
89 | $extra = $this->package->getExtra();
90 | if (isset($extra[self::LABEL_ASSETS_DIR])) {
91 | $assetsDirs = $extra[self::LABEL_ASSETS_DIR];
92 | if (is_string($assetsDirs)) {
93 | // If the assets dir is a string, it targets the whole public directory, we uniform it
94 | $assetsDirs = array(self::LABEL_PUBLIC_DIR => $assetsDirs);
95 | }
96 | // If an assets directory is specified,
97 | // This directory will be used to put
98 | // The other packages assets into
99 | $this->assetsDirectories = $assetsDirs;
100 | }
101 | }
102 |
103 | /**
104 | * Returns the vendor path of the current package
105 | * @return string
106 | */
107 | public function getVendorPath()
108 | {
109 | if (is_null($this->vendorPath)) {
110 | // We get the current package install path
111 | $installPath = $this->composer->getInstallationManager()->getInstallPath($this->package);
112 | // We get the name of the package in order to get its target path
113 | $targetDir = $this->package->getName();
114 | // We remove this target path to isolate the vendor path only
115 | $this->vendorPath = explode($targetDir, $installPath)[0];
116 | }
117 | return $this->vendorPath;
118 | }
119 |
120 | /**
121 | * Installs all the assets directories of the dependency packages
122 | * @return $this
123 | */
124 | public function install()
125 | {
126 | // We get all the packages required
127 | $packages = $this->composer->getPackage()->getRequires();
128 | /** @var Package\Link $package */
129 | foreach ($packages as $package) {
130 | $this->packagesStatuses[$package->getTarget()] = array('extra' => null, 'dirs' => null);
131 | $this->installPackage($package);
132 | }
133 | return $this;
134 | }
135 |
136 | /**
137 | * Installs all the assets directories of the given package
138 | * @param Package\Link $package
139 | */
140 | public function installPackage($package)
141 | {
142 | // We read its composer file
143 | $jsonData = $this->getPackageJsonFile($package);
144 | // We get the package assets dirs
145 | $packagesAssetsDir = $this->getPackageAssetsDirs($jsonData);
146 | if (!is_null($packagesAssetsDir)) {
147 | $this->log($package->getTarget(), self::LOG_INFO, self::CODE_INSTALLING_PACKAGE);
148 | $this->packagesStatuses[$package->getTarget()]['extra'] = 1;
149 | foreach ($packagesAssetsDir as $namespace => $packageAssetsDir) {
150 | if (!isset($this->packagesStatuses[$package->getTarget()]['dirs'])) {
151 | $this->packagesStatuses[$package->getTarget()]['dirs'] = array();
152 | }
153 | $this->packagesStatuses[$package->getTarget()]['dirs'][$namespace] = null;
154 | // We install each package directory
155 | $this->installPackageDir($package, $namespace, $packageAssetsDir);
156 | }
157 | } else {
158 | $this->log($package->getTarget(), self::LOG_INFO, self::CODE_NO_INSTALL);
159 | $this->packagesStatuses[$package->getTarget()]['extra'] = 0;
160 | }
161 | }
162 |
163 | /**
164 | * Returns the Json file of the given Package
165 | * @param Package\Link $package
166 | * @return mixed
167 | */
168 | public function getPackageJsonFile($package)
169 | {
170 | // We get the project vendor path
171 | $vendorPath = $this->getVendorPath();
172 | // We get the target dir of the iterated package
173 | $targetDir = $package->getTarget();
174 | $jsonPath = $vendorPath . $targetDir . "/composer.json";
175 | if (method_exists($package, 'getJsonFile')) {
176 | $jsonFile = $package->getJsonFile($jsonPath);
177 | } else {
178 | // @codeCoverageIgnoreStart
179 | $jsonFile = new JsonFile($jsonPath);
180 | // @codeCoverageIgnoreEnd
181 | }
182 |
183 | return $this->fs->exists($jsonPath) ? $jsonFile->read() : null;
184 | }
185 |
186 | /**
187 | * Returns all the assets directories contained in the given jsonData
188 | * @param array $jsonData
189 | * @return array|null
190 | */
191 | public function getPackageAssetsDirs($jsonData)
192 | {
193 | $packagesAssetsDir = null;
194 | // If some assets were set on the package
195 | if (isset($jsonData["extra"][self::LABEL_ASSETS_DIR])) {
196 | // We get the assets-dir of this package
197 | $packagesAssetsDir = $jsonData["extra"][self::LABEL_ASSETS_DIR];
198 | if (is_string($packagesAssetsDir)) {
199 | // If we get a string, it's the public shortcut so we uniform the return
200 | $packagesAssetsDir = array(self::LABEL_PUBLIC_DIR => $packagesAssetsDir);
201 | }
202 | }
203 | return $packagesAssetsDir;
204 | }
205 |
206 | /**
207 | * Installs a specific directory from the given package
208 | * @param Package\Link $package
209 | * @param string $namespace
210 | * @param string $packageAssetsDir
211 | */
212 | public function installPackageDir($package, $namespace, $packageAssetsDir)
213 | {
214 | $this->log($package->getTarget(), self::LOG_INFO, self::CODE_INSTALLING_DIR, array('namespace' => $namespace));
215 | if (!isset($this->assetsDirectories[$namespace])) {
216 | $this->log($package->getTarget(), self::LOG_WARNING, self::CODE_NO_MATCHING_DIR, array('namespace' => $namespace));
217 | $this->packagesStatuses[$package->getTarget()]['dirs'][$namespace]['status'] = 0;
218 | return;
219 | }
220 | $this->log($package->getTarget(), self::LOG_INFO, self::CODE_MATCHING_DIR, array('namespace' => $namespace));
221 | // We get the project vendor path
222 | $vendorPath = $this->getVendorPath();
223 | // We get the target dir of the iterated package
224 | $targetDir = $package->getTarget();
225 | // We get the full path of the current assets directory
226 | $packagePath = $vendorPath
227 | . $targetDir . "/" . $packageAssetsDir;
228 | // We check if this path is a directory
229 | if ($this->directoryHandler->isDirectory($packagePath)) {
230 | $this->log($package->getTarget(), self::LOG_INFO, self::CODE_DIRECTORY, array('directory' => $this->assetsDirectories[$namespace]));
231 | $src = $packagePath;
232 | $dst = $vendorPath . "../" . $this->assetsDirectories[$namespace] . $targetDir;
233 | // We delete the remaining directory if any
234 | // In order to update it
235 | $this->directoryHandler->deleteDirectory($dst);
236 | // We finally copy the package assets-dir
237 | // Into the project assets-dir
238 | $this->directoryHandler->copyDirectory($src, $dst);
239 | $this->log($package->getTarget(), self::LOG_INFO, self::CODE_DIR_INSTALLED, array('destination' => $dst));
240 | $this->packagesStatuses[$package->getTarget()]['dirs'][$namespace]['status'] = 1;
241 | } else {
242 | $this->log($package->getTarget(), self::LOG_ERROR, self::CODE_NOT_DIRECTORY, array('directory' => $this->assetsDirectories[$namespace]));
243 | $this->packagesStatuses[$package->getTarget()]['dirs'][$namespace]['status'] = 0;
244 | }
245 | }
246 |
247 | private function log($packageName, $type, $code, $extras = array())
248 | {
249 | $message = '';
250 | switch ($code) {
251 | case self::CODE_INSTALLING_DIR:
252 | $message = $packageName . ' : Installing assets : "' . $extras['namespace'] . '"...';
253 | break;
254 | case self::CODE_NO_MATCHING_DIR:
255 | $message = $packageName . ' : Assets directory not set in composer.json : "' . $extras['namespace'] . '"';
256 | break;
257 | case self::CODE_MATCHING_DIR:
258 | $message = $packageName . ' : Assets directory matches : "' . $extras['namespace'] . '"';
259 | break;
260 | case self::CODE_DIRECTORY:
261 | $message = $packageName . ' : Directory found : "' . $extras['directory'] . '"';
262 | break;
263 | case self::CODE_NOT_DIRECTORY:
264 | $message = $packageName . ' : Directory not found : "' . $extras['directory'] . '"';
265 | break;
266 | case self::CODE_DIR_INSTALLED:
267 | $message = $packageName . ' : Directory installed : "' . $extras['destination'] . '"';
268 | break;
269 | case self::CODE_INSTALLING_PACKAGE:
270 | $message = $packageName . ' : Installation in progress...';
271 | break;
272 | case self::CODE_NO_INSTALL:
273 | $message = $packageName . ' : No assets to install';
274 | break;
275 | }
276 | switch ($type) {
277 | case self::LOG_INFO:
278 | $this->io->write(array(' ' . $message . ''));
279 | break;
280 | case self::LOG_WARNING:
281 | $this->io->write(array(' ' . $message . ''));
282 | break;
283 | case self::LOG_ERROR:
284 | $this->io->write(array(' ' . $message . ''));
285 | break;
286 | }
287 | }
288 |
289 | }
290 |
--------------------------------------------------------------------------------
/tests/src/AssetsInstallerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use ReputationVIP\Composer\AssetsInstaller;
13 | use Composer\Config;
14 | use Composer\IO\NullIO;
15 | use Composer\Package;
16 |
17 | class AssetsInstallerTest extends \PHPUnit_Framework_TestCase
18 | {
19 | const NS_DEFAULT = 'default';
20 |
21 | private $mockLinks = array(
22 | self::NS_DEFAULT => array(
23 | array(
24 | 'target' => 'default/package',
25 | 'jsonFile' => array(
26 | 'extra' => array(
27 | 'assets-dir' => 'web'
28 | )
29 | )
30 | )
31 | ),
32 | 'noAssetsDir' => array(
33 | array(
34 | 'target' => 'default/package',
35 | 'jsonFile' => array()
36 | )
37 | )
38 | );
39 |
40 | private $mockDirectoryHandler = array(
41 | self::NS_DEFAULT => array(
42 | 'isDirectory' => true
43 | ),
44 | 'notDirectory' => array(
45 | 'isDirectory' => false
46 | )
47 | );
48 |
49 | private $mockPackage = array(
50 | self::NS_DEFAULT => array(
51 | 'extra' => array(
52 | 'assets-dir' => 'customdir'
53 | ),
54 | 'name' => 'default/package',
55 | 'installPath' => '/tmp/default/package',
56 | 'target' => 'default/package',
57 | 'jsonFile' => array(
58 | 'extra' => array(
59 | 'assets-dir' => 'customdir'
60 | )
61 | )
62 | ),
63 | 'noExtra' => array(
64 | 'extra' => null,
65 | 'name' => 'default/package',
66 | 'installPath' => '/tmp/default/package',
67 | 'target' => 'default/package',
68 | 'jsonFile' => array(
69 | 'extra' => null
70 | )
71 | ),
72 | 'multipleDirs' => array(
73 | 'extra' => array(
74 | 'assets-dir' => array(
75 | 'js' => 'public/js',
76 | 'css' => 'css'
77 | )
78 | ),
79 | 'name' => 'default/package',
80 | 'installPath' => '/tmp/default/package',
81 | 'target' => 'default/package',
82 | 'jsonFile' => array(
83 | 'extra' => array(
84 | 'assets-dir' => array(
85 | 'js' => 'public/js',
86 | 'css' => 'css'
87 | )
88 | )
89 | )
90 | )
91 | );
92 |
93 | public function testShouldFormatAssetsDirToArrayWhenStringIsConfigured()
94 | {
95 | $assetsInstaller = $this->getAssetsInstaller();
96 | $this->assertEquals(array('public' => 'customdir'), $assetsInstaller->assetsDirectories);
97 | }
98 |
99 | public function testShouldSetDefaultAssetsDirWhenNoneIsConfigured()
100 | {
101 | $assetsInstaller = $this->getAssetsInstaller('noExtra');
102 | $this->assertEquals(array('public' => 'public/assets/'), $assetsInstaller->assetsDirectories);
103 | }
104 |
105 | public function testShouldSetMultipleAssetsDirWhenMultipleAreConfigured()
106 | {
107 | $assetsInstaller = $this->getAssetsInstaller('multipleDirs');
108 | $this->assertEquals(array('js' => 'public/js', 'css' => 'css'), $assetsInstaller->assetsDirectories);
109 | }
110 |
111 | public function testShouldCorrectlyGetVendorPath()
112 | {
113 | $assetsInstaller = $this->getAssetsInstaller();
114 | $this->assertEquals('/tmp/', $assetsInstaller->getVendorPath());
115 | }
116 |
117 | public function testShouldReturnThePackageJsonFile()
118 | {
119 | $assetsInstaller = $this->getAssetsInstaller();
120 | $packages = $this->getMockPackagesLinks(self::NS_DEFAULT);
121 | $package = $packages[0];
122 | $expectedJsonFile = array(
123 | 'extra' => array(
124 | 'assets-dir' => 'web'
125 | )
126 | );
127 | $this->assertEquals($expectedJsonFile, $assetsInstaller->getPackageJsonFile($package));
128 | }
129 |
130 | public function testShouldReturnThePublicPackageAssetsDirWhenDirIsString()
131 | {
132 | $assetsInstaller = $this->getAssetsInstaller();
133 | $jsonData = array(
134 | 'extra' => array(
135 | 'assets-dir' => 'web'
136 | )
137 | );
138 | $expectedPackagesAssetsDir = array('public' => 'web');
139 | $this->assertEquals($expectedPackagesAssetsDir, $assetsInstaller->getPackageAssetsDirs($jsonData));
140 | }
141 |
142 | public function testShouldReturnSuccessStatusWhenPackageDirIsInstalled()
143 | {
144 | $assetsInstaller = $this->getAssetsInstaller();
145 | $packages = $this->getMockPackagesLinks(self::NS_DEFAULT);
146 | $package = $packages[0];
147 | $assetsInstaller->installPackageDir($package, 'public', 'web');
148 | $this->assertEquals(1, $assetsInstaller->packagesStatuses['default/package']['dirs']['public']['status']);
149 | }
150 |
151 | public function testShouldReturnErrorStatusWhenAssetsDirIsUndefined()
152 | {
153 | $assetsInstaller = $this->getAssetsInstaller();
154 | $packages = $this->getMockPackagesLinks(self::NS_DEFAULT);
155 | $package = $packages[0];
156 | $assetsInstaller->installPackageDir($package, 'undefined', 'web');
157 | $this->assertEquals(0, $assetsInstaller->packagesStatuses['default/package']['dirs']['undefined']['status']);
158 | }
159 |
160 | public function testShouldReturnErrorStatusWhenPackagePathIsNotADirectory()
161 | {
162 | $assetsInstaller = $this->getAssetsInstaller(self::NS_DEFAULT, self::NS_DEFAULT, 'notDirectory');
163 | $packages = $this->getMockPackagesLinks(self::NS_DEFAULT);
164 | $package = $packages[0];
165 | $assetsInstaller->installPackageDir($package, 'public', 'notADirectory');
166 | $this->assertEquals(0, $assetsInstaller->packagesStatuses['default/package']['dirs']['public']['status']);
167 | }
168 |
169 | public function testShouldInitializeStatusesArrayWhenPackageIsInstalled()
170 | {
171 | $assetsInstaller = $this->getAssetsInstaller();
172 | $packages = $this->getMockPackagesLinks(self::NS_DEFAULT);
173 | $package = $packages[0];
174 | $assetsInstaller->installPackage($package);
175 | $this->assertArrayHasKey('public', $assetsInstaller->packagesStatuses['default/package']['dirs']);
176 | }
177 |
178 | public function testShouldReturnErrorStatusWhenPackageAssetsDirIsNotDefined()
179 | {
180 | $assetsInstaller = $this->getAssetsInstaller();
181 | $packages = $this->getMockPackagesLinks('noAssetsDir');
182 | $package = $packages[0];
183 | $assetsInstaller->installPackage($package);
184 | $this->assertEquals(0, $assetsInstaller->packagesStatuses['default/package']['extra']);
185 | }
186 |
187 | public function testShouldReturnThePackageAssetsDirWhenDirIsArray()
188 | {
189 | $assetsInstaller = $this->getAssetsInstaller();
190 | $jsonData = array(
191 | 'extra' => array(
192 | 'assets-dir' => array(
193 | 'js' => 'web/js',
194 | 'css' => 'web/css'
195 | )
196 | )
197 | );
198 | $expectedPackagesAssetsDir = array(
199 | 'js' => 'web/js',
200 | 'css' => 'web/css'
201 | );
202 | $this->assertEquals($expectedPackagesAssetsDir, $assetsInstaller->getPackageAssetsDirs($jsonData));
203 | }
204 |
205 | public function testShouldReturnSuccessStatusWhenAllPackagesAreInstalled()
206 | {
207 | $assetsInstaller = $this->getAssetsInstaller();
208 | $assetsInstaller->install();
209 | $this->assertEquals(1, $assetsInstaller->packagesStatuses['default/package']['dirs']['public']['status']);
210 | }
211 |
212 |
213 | private function getAssetsInstaller($packageNs = self::NS_DEFAULT, $linksNs = self::NS_DEFAULT, $directoryHandlerNs = self::NS_DEFAULT)
214 | {
215 | $package = $this->getMockPackage($packageNs, $linksNs);
216 | $composer = $this->getComposer($package, $packageNs);
217 | $directoryHandler = $this->getDirectoryHandler($directoryHandlerNs);
218 | $io = $this->getIO();
219 |
220 | $fsStub = $this
221 | ->getMockBuilder('Symfony\Component\Filesystem\Filesystem')
222 | ->getMock();
223 | $fsStub
224 | ->method('exists')
225 | ->willReturn(true);
226 |
227 | return new AssetsInstaller($composer, $io, $directoryHandler, $fsStub);
228 | }
229 |
230 | private function getMockPackage($packageNs = self::NS_DEFAULT, $linksNs = self::NS_DEFAULT)
231 | {
232 | $mockPackageData = $this->mockPackage[$packageNs];
233 |
234 | $jsonFileReader = $this->getMock('JsonFile', array('read'));
235 | $jsonFileReader->expects($this->any())
236 | ->method('read')
237 | ->will($this->returnValue($mockPackageData['jsonFile']));
238 |
239 | $package = $this->getMock('Package', array('getExtra', 'getName', 'getRequires', 'getTarget'));
240 | $package->expects($this->any())
241 | ->method('getExtra')
242 | ->will($this->returnValue($mockPackageData['extra']));
243 | $package->expects($this->any())
244 | ->method('getName')
245 | ->will($this->returnValue($mockPackageData['name']));
246 | $package->expects($this->any())
247 | ->method('getTarget')
248 | ->will($this->returnValue($mockPackageData['target']));
249 | $package->expects($this->any())
250 | ->method('getRequires')
251 | ->will($this->returnValue($this->getMockPackagesLinks($linksNs)));
252 | $package->expects($this->any())
253 | ->method('getJsonFile')
254 | ->will($this->returnValue($jsonFileReader));
255 | return $package;
256 | }
257 |
258 | private function getComposer($package, $packageNs = self::NS_DEFAULT)
259 | {
260 | $mockPackageData = $this->mockPackage[$packageNs];
261 | $installationManager = $this->getMock('InstallationManager', array('getInstallPath'));
262 | $installationManager->expects($this->any())
263 | ->method('getInstallPath')
264 | //->with($package)
265 | ->will($this->returnValue($mockPackageData['installPath']));
266 |
267 | $composer = $this->getMock('Composer', array('getPackage', 'getInstallationManager'));
268 | $composer->expects($this->any())
269 | ->method('getPackage')
270 | ->will($this->returnValue($package));
271 | $composer->expects($this->any())
272 | ->method('getInstallationManager')
273 | ->will($this->returnValue($installationManager));
274 | return $composer;
275 | }
276 |
277 | private function getDirectoryHandler($directoryHandlerNs = self::NS_DEFAULT)
278 | {
279 | $directoryHandlerData = $this->mockDirectoryHandler[$directoryHandlerNs];
280 | $directoryHandler = $this->getMock('DirectoryHandler', array('isDirectory', 'copyDirectory', 'deleteDirectory'));
281 | $directoryHandler->expects($this->any())
282 | ->method('isDirectory')
283 | ->will($this->returnValue($directoryHandlerData['isDirectory']));
284 | return $directoryHandler;
285 | }
286 |
287 | private function getIO()
288 | {
289 | return new NullIO();
290 | }
291 |
292 | private function getMockPackagesLinks($mockLinksNs)
293 | {
294 | $mockLinksData = $this->mockLinks[$mockLinksNs];
295 | $mockLinks = array();
296 | foreach ($mockLinksData as $mockLinkData) {
297 | $mockLinks[] = $this->getMockPackageLink($mockLinkData);
298 | }
299 | return $mockLinks;
300 | }
301 |
302 | private function getMockPackageLink($mockLinkData)
303 | {
304 | $jsonFileReader = $this->getMock('JsonFile', array('read'));
305 | $jsonFileReader->expects($this->any())
306 | ->method('read')
307 | ->will($this->returnValue($mockLinkData['jsonFile']));
308 |
309 | $link = $this->getMock('Link', array('getTarget', 'getJsonFile'));
310 | $link->expects($this->any())
311 | ->method('getTarget')
312 | ->will($this->returnValue($mockLinkData['target']));
313 | $link->expects($this->any())
314 | ->method('getJsonFile')
315 | ->will($this->returnValue($jsonFileReader));
316 |
317 | return $link;
318 | }
319 | }
320 |
--------------------------------------------------------------------------------