feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
297
modules/system/tests/classes/CombineAssetsTest.php
Normal file
297
modules/system/tests/classes/CombineAssetsTest.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Theme;
|
||||
use System\Classes\CombineAssets;
|
||||
|
||||
class CombineAssetsTest extends TestCase
|
||||
{
|
||||
public function setUp() : void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
CombineAssets::resetCache();
|
||||
}
|
||||
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testCombiner()
|
||||
{
|
||||
$combiner = CombineAssets::instance();
|
||||
|
||||
/*
|
||||
* Supported file extensions should exist
|
||||
*/
|
||||
$jsExt = $cssExt = self::getProtectedProperty($combiner, 'jsExtensions');
|
||||
$this->assertIsArray($jsExt);
|
||||
|
||||
$cssExt = self::getProtectedProperty($combiner, 'cssExtensions');
|
||||
$this->assertIsArray($cssExt);
|
||||
|
||||
/*
|
||||
* Check service methods
|
||||
*/
|
||||
$this->assertTrue(method_exists($combiner, 'combine'));
|
||||
$this->assertTrue(method_exists($combiner, 'resetCache'));
|
||||
}
|
||||
|
||||
public function testCombine()
|
||||
{
|
||||
$combiner = CombineAssets::instance();
|
||||
|
||||
$url = $combiner->combine(
|
||||
[
|
||||
'assets/css/style1.css',
|
||||
'assets/css/style2.css'
|
||||
],
|
||||
base_path() . '/modules/system/tests/fixtures/themes/test'
|
||||
);
|
||||
$this->assertNotNull($url);
|
||||
$this->assertRegExp('/\w+[-]\d+/i', $url); // Must contain hash-number
|
||||
|
||||
$url = $combiner->combine(
|
||||
[
|
||||
'assets/js/script1.js',
|
||||
'assets/js/script2.js'
|
||||
],
|
||||
base_path() . '/modules/system/tests/fixtures/themes/test'
|
||||
);
|
||||
$this->assertNotNull($url);
|
||||
$this->assertRegExp('/\w+[-]\d+/i', $url); // Must contain hash-number
|
||||
}
|
||||
|
||||
public function testPutCache()
|
||||
{
|
||||
$sampleId = md5('testhash');
|
||||
$sampleStore = ['version' => 12345678];
|
||||
$samplePath = '/tests/fixtures/Cms/themes/test';
|
||||
|
||||
$combiner = CombineAssets::instance();
|
||||
$value = self::callProtectedMethod($combiner, 'putCache', [$sampleId, $sampleStore]);
|
||||
|
||||
$this->assertTrue($value);
|
||||
}
|
||||
|
||||
public function testGetTargetPath()
|
||||
{
|
||||
$combiner = CombineAssets::instance();
|
||||
|
||||
$value = self::callProtectedMethod($combiner, 'getTargetPath', ['/combine']);
|
||||
$this->assertEquals('combine/', $value);
|
||||
|
||||
$value = self::callProtectedMethod($combiner, 'getTargetPath', ['/index.php/combine']);
|
||||
$this->assertEquals('index-php/combine/', $value);
|
||||
}
|
||||
|
||||
public function testMakeCacheId()
|
||||
{
|
||||
$sampleResources = ['assets/css/style1.css', 'assets/css/style2.css'];
|
||||
$samplePath = base_path() . '/modules/system/tests/fixtures/cms/themes/test';
|
||||
|
||||
$combiner = CombineAssets::instance();
|
||||
self::setProtectedProperty($combiner, 'localPath', $samplePath);
|
||||
|
||||
$value = self::callProtectedMethod($combiner, 'getCacheKey', [$sampleResources]);
|
||||
$this->assertEquals(md5($samplePath.implode('|', $sampleResources)), $value);
|
||||
}
|
||||
|
||||
public function testResetCache()
|
||||
{
|
||||
$combiner = CombineAssets::instance();
|
||||
$this->assertNull($combiner->resetCache());
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression for GHSA-58fp-mcx6-7qf9. A writable theme `.less` file containing
|
||||
* `@import (inline) "<absolute-path>"` must not disclose server files.
|
||||
*/
|
||||
public function testLessCompilerBlocksAbsolutePathImport()
|
||||
{
|
||||
[$themeDir, $secretPath] = $this->setupLessLeakFixture(
|
||||
'@import (inline) "%SECRET%"; .x { color: red; }'
|
||||
);
|
||||
|
||||
try {
|
||||
$css = $this->compileLessTo($themeDir, 'assets/less/poc.less');
|
||||
$this->assertStringNotContainsString('APP_KEY', $css);
|
||||
$this->assertStringNotContainsString('combine-leak-canary', $css);
|
||||
} finally {
|
||||
$this->teardownLessLeakFixture($themeDir, $secretPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression for the relative-traversal path of GHSA-58fp-mcx6-7qf9. less.php's
|
||||
* auto-added `currentDirectory` import_dir entry resolves `..` traversal
|
||||
* natively; without the key-collision override in LessImportResolver, a theme
|
||||
* `.less` could still escape via `@import (inline) "../../../etc/passwd"`.
|
||||
*/
|
||||
public function testLessCompilerBlocksRelativeTraversalImport()
|
||||
{
|
||||
// From themeDir/assets/less/poc.less, traverse up enough to escape the
|
||||
// theme tree, the themes root, and out to the secret file the fixture
|
||||
// wrote at sys_get_temp_dir().
|
||||
[$themeDir, $secretPath] = $this->setupLessLeakFixture(
|
||||
'@import (inline) "' . str_repeat('../', 20) . ltrim($this->lastSecretPath, '/') . '"; .x { color: red; }'
|
||||
);
|
||||
|
||||
try {
|
||||
$css = $this->compileLessTo($themeDir, 'assets/less/poc.less');
|
||||
$this->assertStringNotContainsString('APP_KEY', $css);
|
||||
$this->assertStringNotContainsString('combine-leak-canary', $css);
|
||||
} finally {
|
||||
$this->teardownLessLeakFixture($themeDir, $secretPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legitimate same-tree `@import "partial.less"` must still resolve through
|
||||
* the gate, otherwise we've broken every theme that uses partials.
|
||||
*/
|
||||
public function testLessCompilerAllowsLegitimatePartial()
|
||||
{
|
||||
$themeDir = $this->makeTempThemeDir();
|
||||
$mainPath = $themeDir . '/assets/less/main.less';
|
||||
$partialPath = $themeDir . '/assets/less/partial.less';
|
||||
file_put_contents($partialPath, '.partial-marker { color: orange; }');
|
||||
file_put_contents($mainPath, '@import "partial.less"; .main-marker { color: blue; }');
|
||||
|
||||
try {
|
||||
$css = $this->compileLessTo($themeDir, 'assets/less/main.less');
|
||||
$this->assertStringContainsString('partial-marker', $css);
|
||||
$this->assertStringContainsString('main-marker', $css);
|
||||
} finally {
|
||||
\File::deleteDirectory($themeDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression for GHSA-2223-f22x-24cq. A writable theme `.js` asset containing
|
||||
* `=include ../../../.env` must not disclose server files through the combiner,
|
||||
* whose output is served unauthenticated via the `combine/{file}` route.
|
||||
*/
|
||||
public function testJavascriptImporterBlocksTraversalImport()
|
||||
{
|
||||
$themeDir = $this->makeTempThemeDir();
|
||||
// A real `.js` secret outside the theme subtree (but under base_path so
|
||||
// Assetic's FileAsset root check passes). It escapes via `..` traversal but
|
||||
// lands outside every allowed import root, so it must not be inlined.
|
||||
$secretPath = dirname($themeDir) . '/js-secret-' . bin2hex(random_bytes(4)) . '.js';
|
||||
file_put_contents($secretPath, 'var LEAK = "combine-leak-canary";');
|
||||
file_put_contents(
|
||||
$themeDir . '/assets/poc.js',
|
||||
"/*\n=include ../../" . basename($secretPath) . "\n*/\n"
|
||||
);
|
||||
|
||||
try {
|
||||
$js = $this->compileJsTo($themeDir, 'assets/poc.js');
|
||||
$this->assertStringNotContainsString('combine-leak-canary', $js);
|
||||
} finally {
|
||||
@unlink($secretPath);
|
||||
\File::deleteDirectory($themeDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `.js`-only extension gate must block disclosure of non-JS files (e.g.
|
||||
* `.env`) before any path resolution, even inside an otherwise reachable tree.
|
||||
*/
|
||||
public function testJavascriptImporterBlocksDisallowedExtension()
|
||||
{
|
||||
$themeDir = $this->makeTempThemeDir();
|
||||
$secretPath = dirname($themeDir) . '/js-secret-' . bin2hex(random_bytes(4)) . '.env';
|
||||
file_put_contents($secretPath, "APP_KEY=combine-leak-canary\n");
|
||||
file_put_contents(
|
||||
$themeDir . '/assets/poc.js',
|
||||
"/*\n=include ../../" . basename($secretPath) . "\n*/\n"
|
||||
);
|
||||
|
||||
try {
|
||||
$js = $this->compileJsTo($themeDir, 'assets/poc.js');
|
||||
$this->assertStringNotContainsString('combine-leak-canary', $js);
|
||||
} finally {
|
||||
@unlink($secretPath);
|
||||
\File::deleteDirectory($themeDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legitimate same-tree `=include partial.js` must still resolve, otherwise the
|
||||
* hardening would break every asset that composes its own bundle.
|
||||
*/
|
||||
public function testJavascriptImporterAllowsSameTreeInclude()
|
||||
{
|
||||
$themeDir = $this->makeTempThemeDir();
|
||||
file_put_contents($themeDir . '/assets/partial.js', 'var PARTIAL = "partial-marker";');
|
||||
file_put_contents($themeDir . '/assets/main.js', "/*\n=include partial.js\n*/\nvar MAIN = 1;");
|
||||
|
||||
try {
|
||||
$js = $this->compileJsTo($themeDir, 'assets/main.js');
|
||||
$this->assertStringContainsString('partial-marker', $js);
|
||||
} finally {
|
||||
\File::deleteDirectory($themeDir);
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileJsTo(string $themeDir, string $relativeAsset): string
|
||||
{
|
||||
$dest = sys_get_temp_dir() . '/winter-combine-out-' . bin2hex(random_bytes(4)) . '.js';
|
||||
try {
|
||||
CombineAssets::instance()->combineToFile([$relativeAsset], $dest, $themeDir);
|
||||
return file_get_contents($dest) ?: '';
|
||||
} finally {
|
||||
@unlink($dest);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
protected $lastSecretPath = '';
|
||||
|
||||
/**
|
||||
* @return array{0:string,1:string} [theme dir, secret path]
|
||||
*/
|
||||
protected function setupLessLeakFixture(string $pocTemplate): array
|
||||
{
|
||||
$themeDir = $this->makeTempThemeDir();
|
||||
$secretPath = tempnam(sys_get_temp_dir(), 'combine-leak-canary-');
|
||||
file_put_contents($secretPath, "APP_KEY=do-not-leak-via-combiner\n");
|
||||
$this->lastSecretPath = $secretPath;
|
||||
|
||||
$poc = str_replace('%SECRET%', $secretPath, $pocTemplate);
|
||||
file_put_contents($themeDir . '/assets/less/poc.less', $poc);
|
||||
|
||||
return [$themeDir, $secretPath];
|
||||
}
|
||||
|
||||
protected function teardownLessLeakFixture(string $themeDir, string $secretPath): void
|
||||
{
|
||||
@unlink($secretPath);
|
||||
\File::deleteDirectory($themeDir);
|
||||
}
|
||||
|
||||
protected function makeTempThemeDir(): string
|
||||
{
|
||||
// Must live under base_path() because Assetic's FileAsset enforces that
|
||||
// the source be within the configured root, which CombineAssets sets to
|
||||
// public_path() (equal to base_path() in this install). Using sys_get_temp_dir()
|
||||
// would trigger "source is not in the root directory" errors.
|
||||
$themeDir = base_path('storage/framework/cache/security-tests/theme-' . bin2hex(random_bytes(4)));
|
||||
mkdir($themeDir . '/assets/less', 0777, true);
|
||||
return $themeDir;
|
||||
}
|
||||
|
||||
protected function compileLessTo(string $themeDir, string $relativeAsset): string
|
||||
{
|
||||
$dest = sys_get_temp_dir() . '/winter-combine-out-' . bin2hex(random_bytes(4)) . '.css';
|
||||
try {
|
||||
CombineAssets::instance()->combineToFile([$relativeAsset], $dest, $themeDir);
|
||||
return file_get_contents($dest) ?: '';
|
||||
} finally {
|
||||
@unlink($dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
modules/system/tests/classes/CoreLangTest.php
Normal file
52
modules/system/tests/classes/CoreLangTest.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use System\Classes\PluginManager;
|
||||
use Validator;
|
||||
|
||||
class CoreLangTest extends TestCase
|
||||
{
|
||||
public function testValidationTranslator()
|
||||
{
|
||||
$translator = $this->app['translator'];
|
||||
$translator->setLocale('en');
|
||||
|
||||
$validator = Validator::make(
|
||||
['name' => 'me'],
|
||||
['name' => 'required|min:5']
|
||||
);
|
||||
|
||||
$this->assertTrue($validator->fails());
|
||||
|
||||
$messages = $validator->messages();
|
||||
$this->assertCount(1, $messages);
|
||||
$this->assertEquals('The name must be at least 5 characters.', $messages->all()[0]);
|
||||
}
|
||||
|
||||
public function testValidCoreLanguageFiles()
|
||||
{
|
||||
$translator = $this->app['translator'];
|
||||
$locales = $translator->get('system::lang.locale');
|
||||
$this->assertNotEmpty($locales);
|
||||
|
||||
$locales = array_keys($locales);
|
||||
$modules = ['system', 'backend', 'cms'];
|
||||
$files = ['lang.php', 'validation.php', 'client.php'];
|
||||
|
||||
foreach ($modules as $module) {
|
||||
foreach ($locales as $locale) {
|
||||
foreach ($files as $file) {
|
||||
$srcPath = base_path() . '/modules/'.$module.'/lang/'.$locale.'/'.$file;
|
||||
if (!file_exists($srcPath)) {
|
||||
continue;
|
||||
}
|
||||
$messages = require $srcPath;
|
||||
$this->assertNotEmpty($messages);
|
||||
$this->assertNotCount(0, $messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
modules/system/tests/classes/FileManifestTest.php
Normal file
78
modules/system/tests/classes/FileManifestTest.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Winter\Storm\Exception\ApplicationException;
|
||||
use System\Classes\FileManifest;
|
||||
|
||||
class FileManifestTest extends TestCase
|
||||
{
|
||||
/** @var FileManifest instance */
|
||||
protected $fileManifest;
|
||||
|
||||
/** @var root path */
|
||||
protected $root;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->root = base_path('modules/system/tests/fixtures/manifest/1_0_1');
|
||||
$this->fileManifest = new FileManifest($this->root, ['test', 'test2']);
|
||||
}
|
||||
|
||||
public function testGetFiles()
|
||||
{
|
||||
$this->assertEquals([
|
||||
'/modules/test/file1.php' => '6f9b0b94528a85b2a6bb67b5621e074aef1b4c9fc9ee3ea1bd69100ea14cb3db',
|
||||
'/modules/test/file2.php' => '96ae9f6b6377ad29226ea169f952de49fc29ae895f18a2caed76aeabdf050f1b',
|
||||
'/modules/test2/file1.php' => '94bd47b1ac7b2837b31883ebcd38c8101687321f497c3c4b9744f68ae846721d',
|
||||
], $this->fileManifest->getFiles());
|
||||
}
|
||||
|
||||
public function testGetModuleChecksums()
|
||||
{
|
||||
$this->assertEquals([
|
||||
'test' => 'c0b794ff210862a4ce16223802efe6e28969f5a4fb42480ec8c2fef2da23d181',
|
||||
'test2' => '32c9f2fb6e0a22dde288a0fe1e4834798360b25e5a91d2597409d9302221381d',
|
||||
], $this->fileManifest->getModuleChecksums());
|
||||
}
|
||||
|
||||
public function testGetFilesInvalidRoot()
|
||||
{
|
||||
$this->expectException(ApplicationException::class);
|
||||
$this->expectExceptionMessage('Invalid root specified for the file manifest.');
|
||||
|
||||
$this->fileManifest->setRoot(base_path('tests/fixtures/manifest/invalid'));
|
||||
|
||||
$this->fileManifest->getFiles();
|
||||
}
|
||||
|
||||
public function testSingleModule()
|
||||
{
|
||||
$this->fileManifest->setModules(['test']);
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file1.php' => '6f9b0b94528a85b2a6bb67b5621e074aef1b4c9fc9ee3ea1bd69100ea14cb3db',
|
||||
'/modules/test/file2.php' => '96ae9f6b6377ad29226ea169f952de49fc29ae895f18a2caed76aeabdf050f1b',
|
||||
], $this->fileManifest->getFiles());
|
||||
|
||||
$this->assertEquals([
|
||||
'test' => 'c0b794ff210862a4ce16223802efe6e28969f5a4fb42480ec8c2fef2da23d181',
|
||||
], $this->fileManifest->getModuleChecksums());
|
||||
}
|
||||
|
||||
public function testGetFilename()
|
||||
{
|
||||
$class = new ReflectionClass('System\Classes\FileManifest');
|
||||
$method = $class->getMethod('getFilename');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$filename = '/modules/test/file1.php';
|
||||
|
||||
$this->assertEquals($filename, $method->invoke($this->fileManifest, $this->root . $filename));
|
||||
}
|
||||
}
|
||||
482
modules/system/tests/classes/ImageResizerTest.php
Normal file
482
modules/system/tests/classes/ImageResizerTest.php
Normal file
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use Backend\Facades\Backend;
|
||||
use Cms\Classes\Controller as CmsController;
|
||||
use Cms\Classes\Theme;
|
||||
use Config;
|
||||
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
|
||||
use Event;
|
||||
use Storage;
|
||||
use System\Classes\ImageResizer;
|
||||
use System\Classes\MediaLibrary;
|
||||
use System\Models\File as FileModel;
|
||||
use System\Tests\Bootstrap\PluginTestCase;
|
||||
use URL;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
|
||||
class ImageResizerTest extends PluginTestCase
|
||||
{
|
||||
use ArraySubsetAsserts;
|
||||
|
||||
protected $originalThemesPath = '';
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->originalThemesPath = Config::get('cms.themesPath');
|
||||
Config::set('cms.themesPath', '/modules/system/tests/fixtures/themes');
|
||||
|
||||
Config::set('cms.activeTheme', 'test');
|
||||
Event::flush('cms.theme.getActiveTheme');
|
||||
Theme::resetCache();
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->removeMedia();
|
||||
|
||||
Config::set('cms.themesPath', $this->originalThemesPath);
|
||||
|
||||
ImageResizer::flushAvailableSources();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration through the constructor as well as events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testConfiguration()
|
||||
{
|
||||
if (!in_array('Cms', Config::get('cms.loadModules', []))) {
|
||||
$this->markTestSkipped('The CMS module is not active.');
|
||||
}
|
||||
|
||||
// Resize with default options
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
100,
|
||||
100
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'options' => [
|
||||
'mode' => 'auto',
|
||||
'offset' => [0, 0],
|
||||
'sharpen' => 0,
|
||||
'interlace' => false,
|
||||
'quality' => 90,
|
||||
'extension' => 'png',
|
||||
],
|
||||
], $imageResizer->getConfig());
|
||||
|
||||
// Resize with customised options
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
150,
|
||||
120,
|
||||
[
|
||||
'mode' => 'fit',
|
||||
'offset' => [2, 2],
|
||||
'sharpen' => 23,
|
||||
'interlace' => true,
|
||||
'quality' => 73,
|
||||
'extension' => 'jpg'
|
||||
]
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 150,
|
||||
'height' => 120,
|
||||
'options' => [
|
||||
'mode' => 'fit',
|
||||
'offset' => [2, 2],
|
||||
'sharpen' => 23,
|
||||
'interlace' => true,
|
||||
'quality' => 73,
|
||||
'extension' => 'jpg'
|
||||
],
|
||||
], $imageResizer->getConfig());
|
||||
|
||||
// Resize with an customised defaults
|
||||
Event::listen('system.resizer.getDefaultOptions', function (&$options) {
|
||||
$options = array_merge($options, [
|
||||
'mode' => 'fit',
|
||||
'offset' => [2, 2],
|
||||
'sharpen' => 23,
|
||||
'interlace' => true,
|
||||
'quality' => 73,
|
||||
]);
|
||||
});
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
100,
|
||||
100,
|
||||
[]
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'options' => [
|
||||
'mode' => 'fit',
|
||||
'offset' => [2, 2],
|
||||
'sharpen' => 23,
|
||||
'interlace' => true,
|
||||
'quality' => 73,
|
||||
'extension' => 'png',
|
||||
],
|
||||
], $imageResizer->getConfig());
|
||||
|
||||
Event::forget('system.resizer.getDefaultOptions');
|
||||
|
||||
// Resize with a falsey height specified
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
100,
|
||||
false
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 100,
|
||||
'height' => 0,
|
||||
], $imageResizer->getConfig());
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
100,
|
||||
null
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 100,
|
||||
'height' => 0,
|
||||
], $imageResizer->getConfig());
|
||||
|
||||
// Resize with a falsey width specified
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
'',
|
||||
100
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 0,
|
||||
'height' => 100,
|
||||
], $imageResizer->getConfig());
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
"0",
|
||||
100
|
||||
);
|
||||
self::assertArraySubset([
|
||||
'width' => 0,
|
||||
'height' => 100,
|
||||
], $imageResizer->getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests URLs for sources that can be accessed via URL.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testURLSources()
|
||||
{
|
||||
if (!in_array('Cms', Config::get('cms.loadModules', []))) {
|
||||
$this->markTestSkipped('The CMS module is not active.');
|
||||
}
|
||||
|
||||
// Theme URL (absolute URL)
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
(new CmsController())->themeUrl('assets/images/winter.png'),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Theme URL (relative URL)
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
'/modules/system/tests/fixtures/themes/test/assets/images/winter.png',
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Media URL (absolute URL)
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
URL::to(MediaLibrary::url('winter.png')),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Media URL (relative URL)
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
MediaLibrary::url('winter.png'),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Media URL (absolute URL)
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
URL::to(MediaLibrary::url('winter.png')),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Plugin URL (relative URL)
|
||||
$imageResizer = new ImageResizer(
|
||||
'/modules/system/tests/fixtures/plugins/database/tester/assets/images/avatar.png',
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Plugin URL (absolute URL)
|
||||
$imageResizer = new ImageResizer(
|
||||
URL::to('modules/system/tests/fixtures/plugins/database/tester/assets/images/avatar.png'),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Module URL (relative URL)
|
||||
$imageResizer = new ImageResizer(
|
||||
'/modules/backend/assets/images/favicon.png',
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Module URL (absolute URL)
|
||||
$imageResizer = new ImageResizer(
|
||||
Backend::skinAsset('assets/images/favicon.png'),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// URL for a FileModel instance (absolute URL)
|
||||
$fileModel = new FileModel();
|
||||
$fileModel->fromFile(base_path('modules/system/tests/fixtures/plugins/database/tester/assets/images/avatar.png'));
|
||||
$fileModel->save();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
FileModel::first()->getPath(),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Remove FileModel instance
|
||||
$fileModel->delete();
|
||||
|
||||
// URL of a FileModel instance (relative URL)
|
||||
$fileModel = new FileModel();
|
||||
$fileModel->fromFile(base_path('modules/system/tests/fixtures/plugins/database/tester/assets/images/avatar.png'));
|
||||
$fileModel->save();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
str_replace(url('') . '/', '/', FileModel::first()->getPath()),
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
}
|
||||
|
||||
public function testDirectSources()
|
||||
{
|
||||
// FileModel instance itself
|
||||
$fileModel = new FileModel();
|
||||
$fileModel->fromFile(base_path('modules/system/tests/fixtures/plugins/database/tester/assets/images/avatar.png'));
|
||||
$fileModel->save();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
$fileModel,
|
||||
100,
|
||||
100
|
||||
);
|
||||
$this->assertEquals('png', $imageResizer->getConfig()['options']['extension']);
|
||||
|
||||
// Remove FileModel instance
|
||||
$fileModel->delete();
|
||||
}
|
||||
|
||||
public function testInvalidInputPath()
|
||||
{
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessageMatches('/^Unable to process the provided image/');
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
'/plugins/database/tester/assets/images/MISSING.png',
|
||||
100,
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidInputFileModel()
|
||||
{
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessageMatches('/^Unable to process the provided image/');
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
FileModel::first(),
|
||||
100,
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
public function testSpaceInFilename()
|
||||
{
|
||||
// Media URL with space
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$imageResizer = new ImageResizer(
|
||||
URL::to(MediaLibrary::url('winter space.png')),
|
||||
100,
|
||||
100
|
||||
);
|
||||
|
||||
$this->assertStringContainsString('winter%20space', $imageResizer->getResizedUrl(), 'Resized URLs are not properly URL encoded');
|
||||
}
|
||||
|
||||
public function testGetResizedUrl()
|
||||
{
|
||||
if (!in_array('Cms', Config::get('cms.loadModules', []))) {
|
||||
$this->markTestSkipped('The CMS module is not active.');
|
||||
}
|
||||
|
||||
$imageResizer = new ImageResizer((new CmsController())->themeUrl('assets/images/winter.png'));
|
||||
|
||||
Config::set('cms.linkPolicy', 'force');
|
||||
$url = $imageResizer->getResizedUrl();
|
||||
$this->assertTrue(starts_with($url, 'http'));
|
||||
|
||||
Config::set('cms.linkPolicy', 'detect');
|
||||
$url = $imageResizer->getResizedUrl();
|
||||
$this->assertTrue(starts_with($url, Config::get('cms.storage.resized.path', '/storage/tests/app/resized')));
|
||||
}
|
||||
|
||||
public function testGetResizerUrl()
|
||||
{
|
||||
if (!in_array('Cms', Config::get('cms.loadModules', []))) {
|
||||
$this->markTestSkipped('The CMS module is not active.');
|
||||
}
|
||||
|
||||
$imageResizer = new ImageResizer((new CmsController())->themeUrl('assets/images/winter.png'));
|
||||
|
||||
Config::set('cms.linkPolicy', 'force');
|
||||
$url = $imageResizer->getResizerUrl();
|
||||
$this->assertTrue(starts_with($url, 'http'));
|
||||
|
||||
Config::set('cms.linkPolicy', 'detect');
|
||||
$url = $imageResizer->getResizerUrl();
|
||||
$this->assertTrue(starts_with($url, '/resizer/'));
|
||||
|
||||
// test dots' double-encoding
|
||||
// @see https://github.com/wintercms/winter/pull/1493
|
||||
$this->assertTrue(ends_with($url, '%252Epng'));
|
||||
|
||||
// Verify the encoded URL round-trips through the resizer route's decoding and
|
||||
// signature verification. A fresh instance is required as the identifier is
|
||||
// cached on first generation and the link policy has changed since then. The
|
||||
// router decodes the parameter once before it reaches getValidResizedUrl().
|
||||
$imageResizer = new ImageResizer((new CmsController())->themeUrl('assets/images/winter.png'));
|
||||
[$identifier, $encodedUrl] = array_slice(explode('/', $imageResizer->getResizerUrl()), 2);
|
||||
$this->assertSame(
|
||||
$imageResizer->getResizedUrl(),
|
||||
ImageResizer::getValidResizedUrl($identifier, rawurldecode($encodedUrl))
|
||||
);
|
||||
}
|
||||
|
||||
public function testResizerRedirect()
|
||||
{
|
||||
if (!in_array('Cms', Config::get('cms.loadModules', []))) {
|
||||
$this->markTestSkipped('The CMS module is not active.');
|
||||
}
|
||||
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
Config::set('cms.storage.resized', [
|
||||
'disk' => 'test_local',
|
||||
'folder' => 'resized',
|
||||
'path' => '/storage/temp/app/resized',
|
||||
]);
|
||||
|
||||
$imageResizer = new ImageResizer((new CmsController())->themeUrl('assets/images/winter.png'), 50, 50);
|
||||
|
||||
// The resizer route responds with a permanent redirect as a resizer URL can
|
||||
// only ever target the resized URL embedded and signed within it, and this
|
||||
// also exercises the full round-trip of the double-encoded URL parameter
|
||||
// through the actual router
|
||||
$response = $this->get($imageResizer->getResizerUrl());
|
||||
$response->assertStatus(301);
|
||||
$response->assertRedirect($imageResizer->getResizedUrl());
|
||||
|
||||
// Clean up the generated image
|
||||
Storage::disk('test_local')->deleteDirectory('resized');
|
||||
}
|
||||
|
||||
protected function setUpStorage()
|
||||
{
|
||||
$this->app->useStoragePath(base_path('storage/temp'));
|
||||
|
||||
Config::set('filesystems.disks.test_local', [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app'),
|
||||
]);
|
||||
|
||||
Config::set('cms.storage.media', [
|
||||
'disk' => 'test_local',
|
||||
'folder' => 'media',
|
||||
'path' => '/storage/temp/app/media',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function copyMedia()
|
||||
{
|
||||
$mediaPath = storage_path('app/media');
|
||||
|
||||
if (!is_dir($mediaPath)) {
|
||||
mkdir($mediaPath, 0777, true);
|
||||
}
|
||||
|
||||
foreach (glob(base_path('modules/system/tests/fixtures/media/*')) as $file) {
|
||||
$path = pathinfo($file);
|
||||
copy($file, $mediaPath . DIRECTORY_SEPARATOR . $path['basename']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeMedia()
|
||||
{
|
||||
if ($this->app->storagePath() !== base_path('storage/temp')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (glob(storage_path('app/media/*')) as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
rmdir(storage_path('app/media'));
|
||||
rmdir(storage_path('app'));
|
||||
}
|
||||
}
|
||||
86
modules/system/tests/classes/MarkupManagerTest.php
Normal file
86
modules/system/tests/classes/MarkupManagerTest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use System\Classes\MarkupManager;
|
||||
|
||||
class MarkupManagerTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp() : void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/Plugin.php';
|
||||
}
|
||||
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testIsWildCallable()
|
||||
{
|
||||
$manager = MarkupManager::instance();
|
||||
|
||||
/*
|
||||
* Negatives
|
||||
*/
|
||||
$callable = 'something';
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertFalse($result);
|
||||
|
||||
$callable = ['Form', 'open'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertFalse($result);
|
||||
|
||||
$callable = function () {
|
||||
return 'O, Hai!';
|
||||
};
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertFalse($result);
|
||||
|
||||
/*
|
||||
* String
|
||||
*/
|
||||
$callable = 'something_*';
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'delicious']);
|
||||
$this->assertEquals('something_delicious', $result);
|
||||
|
||||
/*
|
||||
* Array
|
||||
*/
|
||||
$callable = ['Class', 'foo_*'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'bar']);
|
||||
$this->assertArrayHasKey(0, $result);
|
||||
$this->assertArrayHasKey(1, $result);
|
||||
$this->assertEquals('Class', $result[0]);
|
||||
$this->assertEquals('foo_bar', $result[1]);
|
||||
|
||||
$callable = ['My*', 'method'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Class']);
|
||||
$this->assertArrayHasKey(0, $result);
|
||||
$this->assertArrayHasKey(1, $result);
|
||||
$this->assertEquals('MyClass', $result[0]);
|
||||
$this->assertEquals('method', $result[1]);
|
||||
|
||||
$callable = ['My*', 'my*'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Food']);
|
||||
$this->assertArrayHasKey(0, $result);
|
||||
$this->assertArrayHasKey(1, $result);
|
||||
$this->assertEquals('MyFood', $result[0]);
|
||||
$this->assertEquals('myFood', $result[1]);
|
||||
}
|
||||
}
|
||||
172
modules/system/tests/classes/MediaLibraryTest.php
Normal file
172
modules/system/tests/classes/MediaLibraryTest.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use System\Classes\MediaLibrary;
|
||||
|
||||
class MediaLibraryTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
MediaLibrary::forgetInstance();
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->removeMedia();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function invalidPathsProvider()
|
||||
{
|
||||
return [
|
||||
['./file'],
|
||||
['../secret'],
|
||||
['.../secret'],
|
||||
['/../secret'],
|
||||
['/.../secret'],
|
||||
['/secret/..'],
|
||||
['file/../secret'],
|
||||
['file/..'],
|
||||
['......./secret'],
|
||||
['./file'],
|
||||
];
|
||||
}
|
||||
|
||||
public function validPathsProvider()
|
||||
{
|
||||
return [
|
||||
['file'],
|
||||
['folder/file'],
|
||||
['/file'],
|
||||
['/folder/file'],
|
||||
['/.file'],
|
||||
['/..file'],
|
||||
['/...file'],
|
||||
['file.ext'],
|
||||
['file..ext'],
|
||||
['file...ext'],
|
||||
['one,two.ext'],
|
||||
['one(two)[].ext'],
|
||||
['one=(two)[].ext'],
|
||||
['one_(two)[].ext'],
|
||||
/*
|
||||
Example of a unicode-based filename with a single quote
|
||||
@see: https://github.com/octobercms/october/pull/4564
|
||||
*/
|
||||
['BG中国通讯期刊(Blend\'r)创刊号.pdf'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidPathsProvider
|
||||
*/
|
||||
public function testInvalidPathsOnValidatePath($path)
|
||||
{
|
||||
$this->expectException('ApplicationException');
|
||||
MediaLibrary::validatePath($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider validPathsProvider
|
||||
*/
|
||||
public function testValidPathsOnValidatePath($path)
|
||||
{
|
||||
$result = MediaLibrary::validatePath($path);
|
||||
$this->assertIsString($result);
|
||||
}
|
||||
|
||||
public function testListFolderContents()
|
||||
{
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$contents = MediaLibrary::instance()->listFolderContents();
|
||||
$this->assertNotEmpty($contents, 'Media library item is not discovered');
|
||||
$this->assertCount(3, $contents);
|
||||
|
||||
$this->assertEquals('file', $contents[2]->type, 'Media library item does not have the right type');
|
||||
$this->assertEquals('/winter.png', $contents[2]->path, 'Media library item does not have the right path');
|
||||
$this->assertNotEmpty($contents[2]->lastModified, 'Media library item last modified is empty');
|
||||
$this->assertNotEmpty($contents[2]->size, 'Media library item size is empty');
|
||||
|
||||
$this->assertEquals('file', $contents[0]->type, 'Media library item does not have the right type');
|
||||
$this->assertEquals('/text.txt', $contents[0]->path, 'Media library item does not have the right path');
|
||||
$this->assertNotEmpty($contents[0]->lastModified, 'Media library item last modified is empty');
|
||||
$this->assertNotEmpty($contents[0]->size, 'Media library item size is empty');
|
||||
}
|
||||
|
||||
public function testListAllDirectories()
|
||||
{
|
||||
$disk = $this->createConfiguredMock(FilesystemAdapter::class, [
|
||||
'allDirectories' => [
|
||||
'/media/.ignore1',
|
||||
'/media/.ignore2',
|
||||
'/media/dir',
|
||||
'/media/dir/sub',
|
||||
'/media/exclude',
|
||||
'/media/hidden',
|
||||
'/media/hidden/sub1',
|
||||
'/media/hidden/sub1/deep1',
|
||||
'/media/hidden/sub2',
|
||||
'/media/hidden but not really',
|
||||
'/media/name'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->app['config']->set('cms.storage.media.folder', 'media');
|
||||
$this->app['config']->set('cms.storage.media.ignore', ['hidden']);
|
||||
$this->app['config']->set('cms.storage.media.ignorePatterns', ['^\..*']);
|
||||
$instance = MediaLibrary::instance();
|
||||
$this->setProtectedProperty($instance, 'storageDisk', $disk);
|
||||
|
||||
$this->assertEquals(['/', '/dir', '/dir/sub', '/hidden but not really', '/name'], $instance->listAllDirectories(['/exclude']));
|
||||
}
|
||||
|
||||
protected function setUpStorage()
|
||||
{
|
||||
$this->app->useStoragePath(base_path('storage/temp'));
|
||||
|
||||
config(['filesystems.disks.test_local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app'),
|
||||
]]);
|
||||
|
||||
config(['cms.storage.media' => [
|
||||
'disk' => 'test_local',
|
||||
'folder' => 'media',
|
||||
'path' => '/storage/app/media',
|
||||
]]);
|
||||
}
|
||||
|
||||
protected function copyMedia()
|
||||
{
|
||||
$mediaPath = storage_path('app/media');
|
||||
|
||||
if (!is_dir($mediaPath)) {
|
||||
mkdir($mediaPath, 0777, true);
|
||||
}
|
||||
|
||||
foreach (glob(base_path('modules/system/tests/fixtures/media/*')) as $file) {
|
||||
$path = pathinfo($file);
|
||||
copy($file, $mediaPath . DIRECTORY_SEPARATOR . $path['basename']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeMedia()
|
||||
{
|
||||
if ($this->app->storagePath() !== base_path('storage/temp')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (glob(storage_path('app/media/*')) as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
rmdir(storage_path('app/media'));
|
||||
rmdir(storage_path('app'));
|
||||
}
|
||||
}
|
||||
67
modules/system/tests/classes/PluginBaseTest.php
Normal file
67
modules/system/tests/classes/PluginBaseTest.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\PluginManagerTestCase;
|
||||
|
||||
class PluginBaseTest extends PluginManagerTestCase
|
||||
{
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testGetPluginVersions()
|
||||
{
|
||||
$expectedVersions = [
|
||||
'1.0.1' => [
|
||||
'Added some upgrade file and some "seeding"',
|
||||
'some_seeding_file.php' //does not exist
|
||||
],
|
||||
'1.0.2' => [
|
||||
'Added some stuff',
|
||||
],
|
||||
'1.0.3' => [
|
||||
'Bug fix update that uses no scripts'
|
||||
],
|
||||
'1.0.4' => [
|
||||
'Another fix'
|
||||
],
|
||||
'1.0.5' => [
|
||||
'Create blog settings table',
|
||||
'Another update message',
|
||||
'Yet one more update message',
|
||||
],
|
||||
'1.1.0' => [
|
||||
'!!! Drop support for blog settings',
|
||||
],
|
||||
'1.2.0' => [
|
||||
'!!! Security update - see: https://wintercms.com',
|
||||
],
|
||||
'1.3.0' => [
|
||||
'!!! We\'ve refactored major parts of this plugin. Please see the website for more information.',
|
||||
],
|
||||
'1.3.1' => [
|
||||
'Minor bug fix Please see changelog',
|
||||
],
|
||||
'1.3.2' => [
|
||||
'Added support for Translate plugin. Added some new languages.',
|
||||
],
|
||||
'1.4.1' => [
|
||||
'!!! Major update here.',
|
||||
],
|
||||
'1.5.0' => [
|
||||
'!!! Another major update to fix several issues',
|
||||
],
|
||||
'1.5.1' => [
|
||||
'Improved signature with the Test::method()',
|
||||
'Translation updates.',
|
||||
],
|
||||
];
|
||||
|
||||
$plugin = $this->manager->findByIdentifier('Winter.Tester');
|
||||
$versions = $plugin->getPluginVersions(false);
|
||||
|
||||
$this->assertNotNull($versions);
|
||||
$this->assertEquals($expectedVersions, $versions);
|
||||
}
|
||||
}
|
||||
498
modules/system/tests/classes/PluginManagerTest.php
Normal file
498
modules/system/tests/classes/PluginManagerTest.php
Normal file
@@ -0,0 +1,498 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\PluginManagerTestCase;
|
||||
use System\Classes\PluginManager;
|
||||
use System\Classes\PluginBase;
|
||||
|
||||
class PluginManagerTest extends PluginManagerTestCase
|
||||
{
|
||||
const INSTALLED_PLUGIN_COUNT = 17;
|
||||
const ENABLED_PLUGIN_COUNT = 14;
|
||||
const PLUGIN_NAMESPACE_COUNT = 18;
|
||||
const PLUGIN_VENDOR_COUNT = 5;
|
||||
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testLoadPlugins()
|
||||
{
|
||||
$result = $this->manager->loadPlugins();
|
||||
|
||||
$this->assertCount(static::INSTALLED_PLUGIN_COUNT, $result);
|
||||
$this->assertArrayHasKey('Winter.NoUpdates', $result);
|
||||
$this->assertArrayHasKey('Winter.Sample', $result);
|
||||
$this->assertArrayHasKey('Winter.Tester', $result);
|
||||
$this->assertArrayHasKey('Database.Tester', $result);
|
||||
$this->assertArrayHasKey('TestVendor.Test', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.Found', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.NotFound', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.WrongCase', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.Dependency', $result);
|
||||
|
||||
$this->assertArrayNotHasKey('TestVendor.Goto', $result);
|
||||
|
||||
$this->assertInstanceOf('Winter\NoUpdates\Plugin', $result['Winter.NoUpdates']);
|
||||
$this->assertInstanceOf('Winter\Sample\Plugin', $result['Winter.Sample']);
|
||||
$this->assertInstanceOf('Winter\Tester\Plugin', $result['Winter.Tester']);
|
||||
$this->assertInstanceOf('Database\Tester\Plugin', $result['Database.Tester']);
|
||||
$this->assertInstanceOf('TestVendor\Test\Plugin', $result['TestVendor.Test']);
|
||||
$this->assertInstanceOf('DependencyTest\Found\Plugin', $result['DependencyTest.Found']);
|
||||
$this->assertInstanceOf('DependencyTest\NotFound\Plugin', $result['DependencyTest.NotFound']);
|
||||
$this->assertInstanceOf('DependencyTest\WrongCase\Plugin', $result['DependencyTest.WrongCase']);
|
||||
$this->assertInstanceOf('DependencyTest\Dependency\Plugin', $result['DependencyTest.Dependency']);
|
||||
}
|
||||
|
||||
public function testUnloadablePlugin()
|
||||
{
|
||||
$pluginNamespaces = $this->manager->getPluginNamespaces();
|
||||
$result = $this->manager->loadPlugin('\\testvendor\\goto', $pluginNamespaces['\\testvendor\\goto']);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testGetPluginPath()
|
||||
{
|
||||
$result = $this->manager->getPluginPath('Winter\Tester');
|
||||
$basePath = str_replace('\\', '/', base_path());
|
||||
$this->assertEquals($basePath . '/modules/system/tests/fixtures/plugins/winter/tester', $result);
|
||||
}
|
||||
|
||||
public function testGetPlugins()
|
||||
{
|
||||
$result = $this->manager->getPlugins();
|
||||
|
||||
$this->assertCount(static::ENABLED_PLUGIN_COUNT, $result);
|
||||
$this->assertArrayHasKey('Winter.NoUpdates', $result);
|
||||
$this->assertArrayHasKey('Winter.Sample', $result);
|
||||
$this->assertArrayHasKey('Winter.Tester', $result);
|
||||
$this->assertArrayHasKey('Database.Tester', $result);
|
||||
$this->assertArrayHasKey('TestVendor.Test', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.Found', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.WrongCase', $result);
|
||||
$this->assertArrayHasKey('DependencyTest.Dependency', $result);
|
||||
|
||||
$this->assertArrayNotHasKey('DependencyTest.NotFound', $result);
|
||||
$this->assertArrayNotHasKey('TestVendor.Goto', $result);
|
||||
|
||||
$this->assertInstanceOf('Winter\NoUpdates\Plugin', $result['Winter.NoUpdates']);
|
||||
$this->assertInstanceOf('Winter\Sample\Plugin', $result['Winter.Sample']);
|
||||
$this->assertInstanceOf('Winter\Tester\Plugin', $result['Winter.Tester']);
|
||||
$this->assertInstanceOf('Database\Tester\Plugin', $result['Database.Tester']);
|
||||
$this->assertInstanceOf('TestVendor\Test\Plugin', $result['TestVendor.Test']);
|
||||
$this->assertInstanceOf('DependencyTest\Found\Plugin', $result['DependencyTest.Found']);
|
||||
$this->assertInstanceOf('DependencyTest\WrongCase\Plugin', $result['DependencyTest.WrongCase']);
|
||||
$this->assertInstanceOf('DependencyTest\Dependency\Plugin', $result['DependencyTest.Dependency']);
|
||||
}
|
||||
|
||||
public function testFindByNamespace()
|
||||
{
|
||||
$result = $this->manager->findByNamespace('Winter\Tester');
|
||||
$this->assertInstanceOf('Winter\Tester\Plugin', $result);
|
||||
}
|
||||
|
||||
public function testHasPlugin()
|
||||
{
|
||||
$result = $this->manager->hasPlugin('Winter\Tester');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->hasPlugin('DependencyTest.Found');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->hasPlugin('DependencyTest\WrongCase');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->hasPlugin('DependencyTest\NotFound');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->hasPlugin('Winter\XXXXX');
|
||||
$this->assertFalse($result);
|
||||
|
||||
/**
|
||||
* Test case for https://github.com/octobercms/october/pull/4337
|
||||
*/
|
||||
$result = $this->manager->hasPlugin('dependencyTest\Wrongcase');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->hasPlugin('dependencyTest.Wrongcase');
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testGetPluginNamespaces()
|
||||
{
|
||||
$result = $this->manager->getPluginNamespaces();
|
||||
|
||||
$this->assertCount(static::PLUGIN_NAMESPACE_COUNT, $result);
|
||||
$this->assertArrayHasKey('\winter\noupdates', $result);
|
||||
$this->assertArrayHasKey('\winter\sample', $result);
|
||||
$this->assertArrayHasKey('\winter\tester', $result);
|
||||
$this->assertArrayHasKey('\database\tester', $result);
|
||||
$this->assertArrayHasKey('\testvendor\test', $result);
|
||||
$this->assertArrayHasKey('\testvendor\goto', $result);
|
||||
$this->assertArrayHasKey('\dependencytest\found', $result);
|
||||
$this->assertArrayHasKey('\dependencytest\notfound', $result);
|
||||
$this->assertArrayHasKey('\dependencytest\wrongcase', $result);
|
||||
$this->assertArrayHasKey('\dependencytest\dependency', $result);
|
||||
}
|
||||
|
||||
public function testGetVendorAndPluginNames()
|
||||
{
|
||||
$vendors = $this->manager->getVendorAndPluginNames();
|
||||
|
||||
$this->assertCount(static::PLUGIN_VENDOR_COUNT, $vendors);
|
||||
$this->assertArrayHasKey('winter', $vendors);
|
||||
$this->assertArrayHasKey('noupdates', $vendors['winter']);
|
||||
$this->assertArrayHasKey('sample', $vendors['winter']);
|
||||
$this->assertArrayHasKey('tester', $vendors['winter']);
|
||||
|
||||
$this->assertArrayHasKey('database', $vendors);
|
||||
$this->assertArrayHasKey('tester', $vendors['database']);
|
||||
|
||||
$this->assertArrayHasKey('testvendor', $vendors);
|
||||
$this->assertArrayHasKey('test', $vendors['testvendor']);
|
||||
$this->assertArrayHasKey('goto', $vendors['testvendor']);
|
||||
|
||||
$this->assertArrayHasKey('dependencytest', $vendors);
|
||||
$this->assertArrayHasKey('found', $vendors['dependencytest']);
|
||||
$this->assertArrayHasKey('notfound', $vendors['dependencytest']);
|
||||
$this->assertArrayHasKey('wrongcase', $vendors['dependencytest']);
|
||||
$this->assertArrayHasKey('dependency', $vendors['dependencytest']);
|
||||
}
|
||||
|
||||
public function testPluginDetails()
|
||||
{
|
||||
$testPlugin = $this->manager->findByNamespace('Winter\XXXXX');
|
||||
$this->assertNull($testPlugin);
|
||||
|
||||
$testPlugin = $this->manager->findByNamespace('Winter\Tester');
|
||||
$this->assertNotNull($testPlugin);
|
||||
$pluginDetails = $testPlugin->pluginDetails();
|
||||
|
||||
$this->assertEquals('Winter Test Plugin', $pluginDetails['name']);
|
||||
$this->assertEquals('Test plugin used by unit tests.', $pluginDetails['description']);
|
||||
$this->assertEquals('Alexey Bobkov, Samuel Georges', $pluginDetails['author']);
|
||||
}
|
||||
|
||||
public function testUnregisterall()
|
||||
{
|
||||
$result = $this->manager->getPlugins();
|
||||
$this->assertCount(static::ENABLED_PLUGIN_COUNT, $result);
|
||||
|
||||
$this->manager->unregisterAll();
|
||||
$this->assertEmpty($this->manager->getPlugins());
|
||||
}
|
||||
|
||||
public function testGetDependencies()
|
||||
{
|
||||
$result = $this->manager->getDependencies('DependencyTest.Found');
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertContains('DependencyTest.Dependency', $result);
|
||||
|
||||
$result = $this->manager->getDependencies('DependencyTest.WrongCase');
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertContains('Dependencytest.dependency', $result);
|
||||
|
||||
$result = $this->manager->getDependencies('DependencyTest.NotFound');
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertContains('DependencyTest.Missing', $result);
|
||||
}
|
||||
|
||||
public function testIsDisabled()
|
||||
{
|
||||
$result = $this->manager->isDisabled('DependencyTest.Found');
|
||||
$this->assertFalse($result);
|
||||
|
||||
$result = $this->manager->isDisabled('DependencyTest.WrongCase');
|
||||
$this->assertFalse($result);
|
||||
|
||||
$result = $this->manager->isDisabled('DependencyTest.NotFound');
|
||||
$this->assertTrue($result);
|
||||
|
||||
/**
|
||||
* Test case for https://github.com/octobercms/october/pull/4838
|
||||
*/
|
||||
$result = $this->manager->isDisabled('dependencyTest\Wrongcase');
|
||||
$this->assertFalse($result);
|
||||
|
||||
$result = $this->manager->isDisabled('dependencyTest.Wrongcase');
|
||||
$this->assertFalse($result);
|
||||
|
||||
$result = $this->manager->isDisabled('dependencytest.notfound');
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testExists()
|
||||
{
|
||||
$result = $this->manager->exists('DependencyTest.Found');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->exists('DependencyTest.WrongCase');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $this->manager->exists('DependencyTest.NotFound');
|
||||
$this->assertFalse($result);
|
||||
|
||||
$result = $this->manager->exists('Unknown.Plugin');
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testReplacement()
|
||||
{
|
||||
$this->assertFalse($this->manager->isDisabled('Winter.Replacement'));
|
||||
$this->assertTrue($this->manager->isDisabled('Winter.Original'));
|
||||
$this->assertTrue($this->manager->isDisabled('Winter.InvalidReplacement'));
|
||||
|
||||
$this->assertEquals('Winter.Replacement', $this->manager->findByIdentifier('Winter.Original')->getPluginIdentifier());
|
||||
}
|
||||
|
||||
public function testHasPluginReplacement()
|
||||
{
|
||||
// check a replaced plugin
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.Original'));
|
||||
$this->assertTrue($this->manager->isDisabled('Winter.Original'));
|
||||
// check a replacement plugin
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.Replacement'));
|
||||
$this->assertFalse($this->manager->isDisabled('Winter.Replacement'));
|
||||
// check a plugin where the replacement is invalid
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.InvalidReplacement'));
|
||||
$this->assertTrue($this->manager->isDisabled('Winter.InvalidReplacement'));
|
||||
// check a plugin replacing a plugin not found on disk
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.ReplaceNotInstalled'));
|
||||
$this->assertFalse($this->manager->isDisabled('Winter.ReplaceNotInstalled'));
|
||||
// ensure searching for the alias of a replacement (plugin not installed)
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.NotInstalled'));
|
||||
|
||||
$this->assertInstanceOf(\Winter\Replacement\Plugin::class, $this->manager->findByIdentifier('Winter.Original'));
|
||||
$this->assertInstanceOf(\Winter\Replacement\Plugin::class, $this->manager->findByIdentifier('Winter.Replacement'));
|
||||
|
||||
// check getting a plugin via it's not installed original plugin identifier
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.NotInstalled'));
|
||||
$this->assertNull($this->manager->findByIdentifier('Winter.NotInstalled', true));
|
||||
|
||||
// force getting the original plugin
|
||||
$this->assertInstanceOf(\Winter\Original\Plugin::class, $this->manager->findByIdentifier('Winter.Original', true));
|
||||
}
|
||||
|
||||
public function testHasPluginReplacementMixedCase()
|
||||
{
|
||||
// test checking casing of installed plugin (resolved via getNormalizedIdentifier())
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.ReplaceNotInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.replaceNotInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.replacenotInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('winter.replacenotInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('winter.replacenotinstalled'));
|
||||
|
||||
// test checking casing of installed replaced plugin (resolved via getNormalizedIdentifier() & replacementMap)
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.Original'));
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.original'));
|
||||
$this->assertTrue($this->manager->hasPlugin('winter.original'));
|
||||
|
||||
// test checking casing of uninstalled plugin (resolved via strtolower() on replacement keys)
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.NotInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.notInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('winter.notInstalled'));
|
||||
$this->assertTrue($this->manager->hasPlugin('Winter.notinstalled'));
|
||||
}
|
||||
|
||||
public function testExistsReplacementMixedCase()
|
||||
{
|
||||
// test checking casing of installed plugin (resolved via getNormalizedIdentifier())
|
||||
$this->assertTrue($this->manager->exists('Winter.ReplaceNotInstalled'));
|
||||
$this->assertTrue($this->manager->exists('Winter.replaceNotInstalled'));
|
||||
$this->assertTrue($this->manager->exists('Winter.replacenotInstalled'));
|
||||
$this->assertTrue($this->manager->exists('winter.replacenotInstalled'));
|
||||
$this->assertTrue($this->manager->exists('winter.replacenotinstalled'));
|
||||
|
||||
// test checking casing of installed replaced plugin (resolved via getNormalizedIdentifier() & replacementMap)
|
||||
$this->assertFalse($this->manager->exists('Winter.Original'));
|
||||
$this->assertFalse($this->manager->exists('Winter.original'));
|
||||
$this->assertFalse($this->manager->exists('winter.original'));
|
||||
|
||||
// test checking casing of uninstalled plugin (resolved via strtolower() on replacement keys)
|
||||
$this->assertTrue($this->manager->exists('Winter.NotInstalled'));
|
||||
$this->assertTrue($this->manager->exists('Winter.notInstalled'));
|
||||
$this->assertTrue($this->manager->exists('winter.notInstalled'));
|
||||
$this->assertTrue($this->manager->exists('Winter.notinstalled'));
|
||||
}
|
||||
|
||||
public function testFindByIdentifierReplacementMixedCase()
|
||||
{
|
||||
// test resolving plugin with mixed casing
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.ReplaceNotInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.replaceNotInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.replacenotInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('winter.replacenotInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('winter.replacenotinstalled'));
|
||||
|
||||
// test resolving replacement plugin with mixed casing
|
||||
$this->assertInstanceOf(\Winter\Replacement\Plugin::class, $this->manager->findByIdentifier('Winter.Original'));
|
||||
$this->assertInstanceOf(\Winter\Replacement\Plugin::class, $this->manager->findByIdentifier('Winter.original'));
|
||||
$this->assertInstanceOf(\Winter\Replacement\Plugin::class, $this->manager->findByIdentifier('winter.original'));
|
||||
|
||||
// test resolving original plugin with mixed casing when ignoring replacements
|
||||
$this->assertInstanceOf(\Winter\Original\Plugin::class, $this->manager->findByIdentifier('Winter.Original', true));
|
||||
$this->assertInstanceOf(\Winter\Original\Plugin::class, $this->manager->findByIdentifier('Winter.original', true));
|
||||
$this->assertInstanceOf(\Winter\Original\Plugin::class, $this->manager->findByIdentifier('winter.original', true));
|
||||
|
||||
// test resolving replacement plugin of uninstalled plugin with mixed casing
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.NotInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.notInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('winter.notInstalled'));
|
||||
$this->assertInstanceOf(\Winter\ReplaceNotInstalled\Plugin::class, $this->manager->findByIdentifier('Winter.notinstalled'));
|
||||
}
|
||||
|
||||
public function testGetReplacements()
|
||||
{
|
||||
$replacementPluginReplaces = $this->manager->findByIdentifier('Winter.Replacement')->getReplaces();
|
||||
|
||||
$this->assertIsArray($replacementPluginReplaces);
|
||||
$this->assertCount(1, $replacementPluginReplaces);
|
||||
$this->assertEquals('Winter.Original', $replacementPluginReplaces[0]);
|
||||
|
||||
$invalidPluginReplaces = $this->manager->findByIdentifier('Winter.InvalidReplacement')->getReplaces();
|
||||
|
||||
$this->assertIsArray($invalidPluginReplaces);
|
||||
$this->assertCount(1, $invalidPluginReplaces);
|
||||
$this->assertEquals('Winter.Tester', $invalidPluginReplaces[0]);
|
||||
}
|
||||
|
||||
public function testReplaceVersion()
|
||||
{
|
||||
$invalidReplacementPlugin = $this->manager->findByIdentifier('Winter.InvalidReplacement');
|
||||
|
||||
$this->assertTrue($invalidReplacementPlugin->canReplacePlugin('Winter.Tester', '9'));
|
||||
$this->assertTrue($invalidReplacementPlugin->canReplacePlugin('Winter.Tester', '9.0'));
|
||||
$this->assertTrue($invalidReplacementPlugin->canReplacePlugin('Winter.Tester', '11.0.0'));
|
||||
$this->assertFalse($invalidReplacementPlugin->canReplacePlugin('Winter.Tester', '8.0'));
|
||||
|
||||
$replacementPlugin = $this->manager->findByIdentifier('Winter.Replacement');
|
||||
|
||||
$this->assertTrue($replacementPlugin->canReplacePlugin('Winter.Original', '1.0.2'));
|
||||
$this->assertTrue($replacementPlugin->canReplacePlugin('Winter.Original', '1.0'));
|
||||
$this->assertFalse($replacementPlugin->canReplacePlugin('Winter.Original', '2.0.1'));
|
||||
}
|
||||
|
||||
public function testActiveReplacementMap()
|
||||
{
|
||||
$map = $this->manager->getActiveReplacementMap();
|
||||
$this->assertArrayHasKey('Winter.Original', $map);
|
||||
$this->assertEquals('Winter.Replacement', $map['Winter.Original']);
|
||||
|
||||
$this->assertEquals('Winter.Replacement', $this->manager->getActiveReplacementMap('Winter.Original'));
|
||||
$this->assertNull($this->manager->getActiveReplacementMap('Winter.InvalidReplacement'));
|
||||
}
|
||||
|
||||
public function testFlagDisableStatus()
|
||||
{
|
||||
$plugin = $this->manager->findByIdentifier('DependencyTest.Dependency');
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertEmpty($flags);
|
||||
|
||||
$plugin = $this->manager->findByIdentifier('DependencyTest.NotFound');
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertCount(1, $flags);
|
||||
$this->assertArrayHasKey(PluginManager::DISABLED_MISSING_DEPENDENCIES, $flags);
|
||||
|
||||
$plugin = $this->manager->findByIdentifier('Winter.InvalidReplacement');
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertCount(1, $flags);
|
||||
$this->assertArrayHasKey(PluginManager::DISABLED_REPLACEMENT_FAILED, $flags);
|
||||
|
||||
$plugin = $this->manager->findByIdentifier('Winter.Original', true);
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertCount(1, $flags);
|
||||
$this->assertArrayHasKey(PluginManager::DISABLED_REPLACED, $flags);
|
||||
}
|
||||
|
||||
public function testFlagDisabling()
|
||||
{
|
||||
$plugin = $this->manager->findByIdentifier('Winter.Tester', true);
|
||||
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertEmpty($flags);
|
||||
|
||||
$this->manager->disablePlugin($plugin);
|
||||
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertCount(1, $flags);
|
||||
$this->assertArrayHasKey(PluginManager::DISABLED_BY_USER, $flags);
|
||||
|
||||
$this->manager->enablePlugin($plugin);
|
||||
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertEmpty($flags);
|
||||
|
||||
$this->manager->disablePlugin($plugin, PluginManager::DISABLED_BY_CONFIG);
|
||||
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertCount(1, $flags);
|
||||
$this->assertArrayHasKey(PluginManager::DISABLED_BY_CONFIG, $flags);
|
||||
|
||||
$this->manager->enablePlugin($plugin, PluginManager::DISABLED_BY_CONFIG);
|
||||
|
||||
$flags = $this->manager->getPluginFlags($plugin);
|
||||
$this->assertEmpty($flags);
|
||||
}
|
||||
|
||||
public function testPluginNormalization()
|
||||
{
|
||||
// test lower to upper
|
||||
$this->assertEquals('Database.Tester', $this->manager->normalizeIdentifier('database.tester'));
|
||||
$this->assertEquals('Database.Tester', $this->manager->getNormalizedIdentifier('database.tester'));
|
||||
|
||||
// test exact match
|
||||
$this->assertEquals('DependencyTest.Found', $this->manager->normalizeIdentifier('DependencyTest.Found'));
|
||||
$this->assertEquals('DependencyTest.Found', $this->manager->getNormalizedIdentifier('DependencyTest.Found'));
|
||||
|
||||
// test mixed case
|
||||
$this->assertEquals('DependencyTest.Found', $this->manager->normalizeIdentifier('Dependencytest.Found'));
|
||||
$this->assertEquals('DependencyTest.Found', $this->manager->getNormalizedIdentifier('Dependencytest.Found'));
|
||||
|
||||
// test typeo
|
||||
$this->assertEquals('dpendencytest.Found', $this->manager->normalizeIdentifier('dpendencytest.Found'));
|
||||
$this->assertEquals('dpendencytest.Found', $this->manager->getNormalizedIdentifier('dpendencytest.Found'));
|
||||
$this->assertEquals('Winter.NoUpdate', $this->manager->normalizeIdentifier('Winter.NoUpdate'));
|
||||
$this->assertEquals('Winter.NoUpdate', $this->manager->getNormalizedIdentifier('Winter.NoUpdate'));
|
||||
|
||||
// test multiple mixed case installed plugin
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->normalizeIdentifier('Winter.NoUpdates'));
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->normalizeIdentifier('winter.noUpdates'));
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->normalizeIdentifier('winter.noupdates'));
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->getNormalizedIdentifier('Winter.NoUpdates'));
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->getNormalizedIdentifier('winter.noUpdates'));
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->getNormalizedIdentifier('winter.noupdates'));
|
||||
|
||||
// test multiple mixed case not installed plugin
|
||||
$this->assertEquals('Winter.MissingPlugin', $this->manager->normalizeIdentifier('Winter.MissingPlugin'));
|
||||
$this->assertEquals('Winter.Missingplugin', $this->manager->normalizeIdentifier('Winter.Missingplugin'));
|
||||
$this->assertEquals('Winter.missingplugin', $this->manager->normalizeIdentifier('Winter.missingplugin'));
|
||||
$this->assertEquals('winter.missingplugin', $this->manager->normalizeIdentifier('winter.missingplugin'));
|
||||
$this->assertEquals('Winter.MissingPlugin', $this->manager->getNormalizedIdentifier('Winter.MissingPlugin'));
|
||||
$this->assertEquals('Winter.Missingplugin', $this->manager->getNormalizedIdentifier('Winter.Missingplugin'));
|
||||
$this->assertEquals('Winter.missingplugin', $this->manager->getNormalizedIdentifier('Winter.missingplugin'));
|
||||
$this->assertEquals('winter.missingplugin', $this->manager->getNormalizedIdentifier('winter.missingplugin'));
|
||||
|
||||
// test passing plugin object
|
||||
$plugin = $this->manager->findByIdentifier('Winter.NoUpdates');
|
||||
$this->assertInstanceOf(PluginBase::class, $plugin);
|
||||
|
||||
$this->assertEquals('Winter.NoUpdates', $this->manager->getNormalizedIdentifier($plugin));
|
||||
}
|
||||
|
||||
public function testSortPluginDependencies()
|
||||
{
|
||||
$result = $this->manager->getPlugins();
|
||||
|
||||
$this->assertGreaterThan(
|
||||
array_search('DependencyTest.Dependency', array_keys($result)),
|
||||
array_search('DependencyTest.Found', array_keys($result))
|
||||
);
|
||||
|
||||
// check to make sure dependency comes first and didn't stay in alphanumeric order.
|
||||
$this->assertGreaterThan(
|
||||
array_search('DependencyTest.Dependency', array_keys($result)),
|
||||
array_search('DependencyTest.Acme', array_keys($result))
|
||||
);
|
||||
}
|
||||
}
|
||||
341
modules/system/tests/classes/SourceManifestTest.php
Normal file
341
modules/system/tests/classes/SourceManifestTest.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Winter\Storm\Argon\Argon;
|
||||
use System\Classes\SourceManifest;
|
||||
use System\Classes\FileManifest;
|
||||
|
||||
class SourceManifestTest extends TestCase
|
||||
{
|
||||
/** @var SourceManifest instance */
|
||||
protected $sourceManifest;
|
||||
|
||||
/** @var array Emulated builds from the manifest fixture */
|
||||
protected $builds;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->builds = [
|
||||
'1.0.0' => new FileManifest(
|
||||
base_path('modules/system/tests/fixtures/manifest/1_0_0'),
|
||||
['test', 'test2']
|
||||
),
|
||||
'1.0.1' => new FileManifest(
|
||||
base_path('modules/system/tests/fixtures/manifest/1_0_1'),
|
||||
['test', 'test2']
|
||||
),
|
||||
'1.0.2' => new FileManifest(
|
||||
base_path('modules/system/tests/fixtures/manifest/1_0_2'),
|
||||
['test', 'test2']
|
||||
),
|
||||
'1.1.0' => new FileManifest(
|
||||
base_path('modules/system/tests/fixtures/manifest/1_1_0'),
|
||||
['test', 'test2', 'test3']
|
||||
),
|
||||
'1.1.1' => new FileManifest(
|
||||
base_path('modules/system/tests/fixtures/manifest/1_1_1'),
|
||||
['test', 'test2', 'test3']
|
||||
),
|
||||
'1.0.3' => new FileManifest(
|
||||
base_path('modules/system/tests/fixtures/manifest/1_0_3'),
|
||||
['test', 'test2']
|
||||
),
|
||||
];
|
||||
|
||||
$this->sourceManifest = new SourceManifest($this->manifestPath(), $this->forksPath(), false);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
Argon::setTestNow();
|
||||
$this->deleteManifest();
|
||||
}
|
||||
|
||||
public function testCreateManifest()
|
||||
{
|
||||
// Freeze date for test
|
||||
$testDate = Argon::create(2020, 12, 16, 12, 01, 0, 'UTC');
|
||||
Argon::setTestNow($testDate);
|
||||
|
||||
$this->createManifest(true);
|
||||
|
||||
$this->assertEquals(
|
||||
'{' . "\n" .
|
||||
' "_description": "This is the source manifest of changes to Winter CMS for each version. This is used to determine which version of Winter CMS is in use, via the \"winter:version\" Artisan command.",' . "\n" .
|
||||
' "_created": "2020-12-16T12:01:00+00:00",' . "\n" .
|
||||
' "manifest": [' . "\n" .
|
||||
' {' . "\n" .
|
||||
' "build": "1.0.0",' . "\n" .
|
||||
' "parent": null,' . "\n" .
|
||||
' "modules": {' . "\n" .
|
||||
' "test": "e1d6c6e4c482688e231ee37d89668268426512013695de47bfcb424f9a645c7b",' . "\n" .
|
||||
' "test2": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "files": {' . "\n" .
|
||||
' "added": {' . "\n" .
|
||||
' "\/modules\/test\/file1.php": "6f9b0b94528a85b2a6bb67b5621e074aef1b4c9fc9ee3ea1bd69100ea14cb3db"' . "\n" .
|
||||
' }' . "\n" .
|
||||
' }' . "\n" .
|
||||
' },' . "\n" .
|
||||
' {' . "\n" .
|
||||
' "build": "1.0.1",' . "\n" .
|
||||
' "parent": "1.0.0",' . "\n" .
|
||||
' "modules": {' . "\n" .
|
||||
' "test": "c0b794ff210862a4ce16223802efe6e28969f5a4fb42480ec8c2fef2da23d181",' . "\n" .
|
||||
' "test2": "32c9f2fb6e0a22dde288a0fe1e4834798360b25e5a91d2597409d9302221381d"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "files": {' . "\n" .
|
||||
' "added": {' . "\n" .
|
||||
' "\/modules\/test\/file2.php": "96ae9f6b6377ad29226ea169f952de49fc29ae895f18a2caed76aeabdf050f1b",' . "\n" .
|
||||
' "\/modules\/test2\/file1.php": "94bd47b1ac7b2837b31883ebcd38c8101687321f497c3c4b9744f68ae846721d"' . "\n" .
|
||||
' }' . "\n" .
|
||||
' }' . "\n" .
|
||||
' },' . "\n" .
|
||||
' {' . "\n" .
|
||||
' "build": "1.0.2",' . "\n" .
|
||||
' "parent": "1.0.1",' . "\n" .
|
||||
' "modules": {' . "\n" .
|
||||
' "test": "419a3c073a4296213cdc9319cfc488383753e2e81cefa1c73db38749b82a3c51",' . "\n" .
|
||||
' "test2": "32c9f2fb6e0a22dde288a0fe1e4834798360b25e5a91d2597409d9302221381d"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "files": {' . "\n" .
|
||||
' "added": {' . "\n" .
|
||||
' "\/modules\/test\/file3.php": "7f4132b05911a6b0df4d41bf5dc3d007786b63a5a22daf3060ed222816d57b54"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "modified": {' . "\n" .
|
||||
' "\/modules\/test\/file2.php": "2c61b2f5688275574251a19a57e06a4eb9e537b3916ebf6f71768e184a4ae538"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "removed": [' . "\n" .
|
||||
' "\/modules\/test\/file1.php"' . "\n" .
|
||||
' ]' . "\n" .
|
||||
' }' . "\n" .
|
||||
' },' . "\n" .
|
||||
' {' . "\n" .
|
||||
' "build": "1.0.3",' . "\n" .
|
||||
' "parent": "1.0.2",' . "\n" .
|
||||
' "modules": {' . "\n" .
|
||||
' "test": "5316f172ac24aaa7713c97885e2e27f5c5c1e02f96fcb8e269903d737d52c7bd",' . "\n" .
|
||||
' "test2": "18fc53cb280cc7e43d47e55a66b1245992c331f368106c13024572dc9fb215f7"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "files": {' . "\n" .
|
||||
' "modified": {' . "\n" .
|
||||
' "\/modules\/test2\/file1.php": "e284e816365653b5f7ddfca9319c24716cadfa3538e0c79994f0416e964da513"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "removed": [' . "\n" .
|
||||
' "\/modules\/test\/file3.php"' . "\n" .
|
||||
' ]' . "\n" .
|
||||
' }' . "\n" .
|
||||
' },' . "\n" .
|
||||
' {' . "\n" .
|
||||
' "build": "1.1.0",' . "\n" .
|
||||
' "parent": "1.0.2",' . "\n" .
|
||||
' "modules": {' . "\n" .
|
||||
' "test": "e30811c9ad3119394edefd2f2fc0bae5fc08e2a03220ccc94e2a3f078da0df6d",' . "\n" .
|
||||
' "test2": "2066b3cffe4f03b06c3399dba4d56147e4247b8071e19fa597a2e15dec697f5e",' . "\n" .
|
||||
' "test3": "3679fa86c8d92a4474a01fc41abc24ebf34b847d7288434028a0559a10ff5d33"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "files": {' . "\n" .
|
||||
' "added": {' . "\n" .
|
||||
' "\/modules\/test\/file4.php": "18047babf2625ed8f42d779f14539449e23ccfdc92f79818f291eb1c55ff0533",' . "\n" .
|
||||
' "\/modules\/test3\/file1.php": "1b691b48b8247af3dd8046a70e5678b77e66ada13b86c3a269a6428425c3a835"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "modified": {' . "\n" .
|
||||
' "\/modules\/test\/file3.php": "65b1d6f8a1a0f2dfbe870faba9fa2ee0b6f4c291656df6cd66158e166a2680eb",' . "\n" .
|
||||
' "\/modules\/test2\/file1.php": "5197bcc06443799c71fea8cb608cee61d9edd611ea23ea603aa0420393e014ca"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "removed": [' . "\n" .
|
||||
' "\/modules\/test\/file2.php"' . "\n" .
|
||||
' ]' . "\n" .
|
||||
' }' . "\n" .
|
||||
' },' . "\n" .
|
||||
' {' . "\n" .
|
||||
' "build": "1.1.1",' . "\n" .
|
||||
' "parent": "1.1.0",' . "\n" .
|
||||
' "modules": {' . "\n" .
|
||||
' "test": "e30811c9ad3119394edefd2f2fc0bae5fc08e2a03220ccc94e2a3f078da0df6d",' . "\n" .
|
||||
' "test2": "18fc53cb280cc7e43d47e55a66b1245992c331f368106c13024572dc9fb215f7",' . "\n" .
|
||||
' "test3": "728ff099502e8d40cc6a432fdeba7185b9cb5cb530dcd8f5741ec56fcd37d189"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "files": {' . "\n" .
|
||||
' "added": {' . "\n" .
|
||||
' "\/modules\/test3\/file2.php": "0614afe438a4e74547a7036ef895008c36895a783eb8e0b458691f631138d310"' . "\n" .
|
||||
' },' . "\n" .
|
||||
' "modified": {' . "\n" .
|
||||
' "\/modules\/test2\/file1.php": "e284e816365653b5f7ddfca9319c24716cadfa3538e0c79994f0416e964da513",' . "\n" .
|
||||
' "\/modules\/test3\/file1.php": "d9aeb0853bc0f21a836f2d1cd5fc224d64ce8ea9e7e22c43b1a00f25d37c42f8"' . "\n" .
|
||||
' }' . "\n" .
|
||||
' }' . "\n" .
|
||||
' }' . "\n" .
|
||||
' ]' . "\n" .
|
||||
'}',
|
||||
file_get_contents($this->manifestPath())
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetBuilds()
|
||||
{
|
||||
$this->createManifest();
|
||||
|
||||
$buildKeys = $this->sourceManifest->getBuilds();
|
||||
|
||||
$this->assertCount(6, $buildKeys);
|
||||
$this->assertEquals(['1.0.0', '1.0.1', '1.0.2', '1.0.3', '1.1.0', '1.1.1'], $buildKeys);
|
||||
}
|
||||
|
||||
public function testGetState()
|
||||
{
|
||||
$this->createManifest();
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file1.php' => '6f9b0b94528a85b2a6bb67b5621e074aef1b4c9fc9ee3ea1bd69100ea14cb3db',
|
||||
], $this->sourceManifest->getState('1.0.0'));
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file1.php' => '6f9b0b94528a85b2a6bb67b5621e074aef1b4c9fc9ee3ea1bd69100ea14cb3db',
|
||||
'/modules/test/file2.php' => '96ae9f6b6377ad29226ea169f952de49fc29ae895f18a2caed76aeabdf050f1b',
|
||||
'/modules/test2/file1.php' => '94bd47b1ac7b2837b31883ebcd38c8101687321f497c3c4b9744f68ae846721d',
|
||||
], $this->sourceManifest->getState('1.0.1'));
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file2.php' => '2c61b2f5688275574251a19a57e06a4eb9e537b3916ebf6f71768e184a4ae538',
|
||||
'/modules/test/file3.php' => '7f4132b05911a6b0df4d41bf5dc3d007786b63a5a22daf3060ed222816d57b54',
|
||||
'/modules/test2/file1.php' => '94bd47b1ac7b2837b31883ebcd38c8101687321f497c3c4b9744f68ae846721d',
|
||||
], $this->sourceManifest->getState('1.0.2'));
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file2.php' => '2c61b2f5688275574251a19a57e06a4eb9e537b3916ebf6f71768e184a4ae538',
|
||||
'/modules/test2/file1.php' => 'e284e816365653b5f7ddfca9319c24716cadfa3538e0c79994f0416e964da513',
|
||||
], $this->sourceManifest->getState('1.0.3'));
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file3.php' => '65b1d6f8a1a0f2dfbe870faba9fa2ee0b6f4c291656df6cd66158e166a2680eb',
|
||||
'/modules/test/file4.php' => '18047babf2625ed8f42d779f14539449e23ccfdc92f79818f291eb1c55ff0533',
|
||||
'/modules/test2/file1.php' => '5197bcc06443799c71fea8cb608cee61d9edd611ea23ea603aa0420393e014ca',
|
||||
'/modules/test3/file1.php' => '1b691b48b8247af3dd8046a70e5678b77e66ada13b86c3a269a6428425c3a835',
|
||||
], $this->sourceManifest->getState('1.1.0'));
|
||||
|
||||
$this->assertEquals([
|
||||
'/modules/test/file3.php' => '65b1d6f8a1a0f2dfbe870faba9fa2ee0b6f4c291656df6cd66158e166a2680eb',
|
||||
'/modules/test/file4.php' => '18047babf2625ed8f42d779f14539449e23ccfdc92f79818f291eb1c55ff0533',
|
||||
'/modules/test2/file1.php' => 'e284e816365653b5f7ddfca9319c24716cadfa3538e0c79994f0416e964da513',
|
||||
'/modules/test3/file1.php' => 'd9aeb0853bc0f21a836f2d1cd5fc224d64ce8ea9e7e22c43b1a00f25d37c42f8',
|
||||
'/modules/test3/file2.php' => '0614afe438a4e74547a7036ef895008c36895a783eb8e0b458691f631138d310',
|
||||
], $this->sourceManifest->getState('1.1.1'));
|
||||
}
|
||||
|
||||
public function testCompare()
|
||||
{
|
||||
$this->createManifest();
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.0.0',
|
||||
'modified' => false,
|
||||
'confident' => true
|
||||
], $this->sourceManifest->compare($this->builds['1.0.0']));
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.0.1',
|
||||
'modified' => false,
|
||||
'confident' => true
|
||||
], $this->sourceManifest->compare($this->builds['1.0.1']));
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.0.2',
|
||||
'modified' => false,
|
||||
'confident' => true
|
||||
], $this->sourceManifest->compare($this->builds['1.0.2']));
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.0.3',
|
||||
'modified' => false,
|
||||
'confident' => true
|
||||
], $this->sourceManifest->compare($this->builds['1.0.3']));
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.1.0',
|
||||
'modified' => false,
|
||||
'confident' => true
|
||||
], $this->sourceManifest->compare($this->builds['1.1.0']));
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.1.1',
|
||||
'modified' => false,
|
||||
'confident' => true
|
||||
], $this->sourceManifest->compare($this->builds['1.1.1']));
|
||||
}
|
||||
|
||||
public function testCompareModified()
|
||||
{
|
||||
$this->createManifest();
|
||||
|
||||
// Hot-swap "tests/fixtures/manifest/1_1_1/modules/test/file3.php"
|
||||
$old = file_get_contents(base_path('modules/system/tests/fixtures/manifest/1_1_1/modules/test/file3.php'));
|
||||
file_put_contents(base_path('modules/system/tests/fixtures/manifest/1_1_1/modules/test/file3.php'), '<?php // Changed');
|
||||
|
||||
$modifiedManifest = new FileManifest(base_path('modules/system/tests/fixtures/manifest/1_1_1'), ['test', 'test2']);
|
||||
$modifiedManifest->getFiles();
|
||||
|
||||
file_put_contents(base_path('modules/system/tests/fixtures/manifest/1_1_1/modules/test/file3.php'), $old);
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.1.1',
|
||||
'modified' => true,
|
||||
'confident' => true,
|
||||
], $this->sourceManifest->compare($modifiedManifest));
|
||||
}
|
||||
|
||||
public function testCompareModifiedSecondBranch()
|
||||
{
|
||||
$this->createManifest();
|
||||
|
||||
// Add "tests/fixtures/manifest/1_0_3/modules/test/file3.php"
|
||||
file_put_contents(base_path('modules/system/tests/fixtures/manifest/1_0_3/modules/test/file3.php'), '<?php // Changed');
|
||||
|
||||
$modifiedManifest = new FileManifest(base_path('modules/system/tests/fixtures/manifest/1_0_3'), ['test', 'test2']);
|
||||
$modifiedManifest->getFiles();
|
||||
|
||||
unlink(base_path('modules/system/tests/fixtures/manifest/1_0_3/modules/test/file3.php'));
|
||||
|
||||
$this->assertEquals([
|
||||
'build' => '1.0.3',
|
||||
'modified' => true,
|
||||
'confident' => false, // 50% match, not confident
|
||||
], $this->sourceManifest->compare($modifiedManifest));
|
||||
}
|
||||
|
||||
protected function createManifest(bool $write = false)
|
||||
{
|
||||
$this->deleteManifest();
|
||||
|
||||
foreach ($this->builds as $build => $fileManifest) {
|
||||
$this->sourceManifest->addBuild($build, $fileManifest);
|
||||
}
|
||||
|
||||
if ($write) {
|
||||
file_put_contents($this->manifestPath(), $this->sourceManifest->generate());
|
||||
}
|
||||
|
||||
$this->sourceManifest->loadForks();
|
||||
}
|
||||
|
||||
protected function deleteManifest()
|
||||
{
|
||||
if (file_exists($this->manifestPath())) {
|
||||
unlink($this->manifestPath());
|
||||
}
|
||||
}
|
||||
|
||||
protected function manifestPath()
|
||||
{
|
||||
return base_path('modules/system/tests/fixtures/manifest/builds.json');
|
||||
}
|
||||
|
||||
protected function forksPath()
|
||||
{
|
||||
return base_path('modules/system/tests/fixtures/manifest/forks.json');
|
||||
}
|
||||
}
|
||||
258
modules/system/tests/classes/VersionManagerTest.php
Normal file
258
modules/system/tests/classes/VersionManagerTest.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use System\Classes\VersionManager;
|
||||
|
||||
class VersionManagerTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp() : void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/Plugin.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/sample/Plugin.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/noupdates/Plugin.php';
|
||||
}
|
||||
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testGetLatestFileVersion()
|
||||
{
|
||||
$manager = VersionManager::instance();
|
||||
$result = self::callProtectedMethod($manager, 'getLatestFileVersion', ['\Winter\\Tester']);
|
||||
|
||||
$this->assertNotNull($result);
|
||||
$this->assertEquals('1.5.1', $result);
|
||||
}
|
||||
|
||||
public function testGetFileVersions()
|
||||
{
|
||||
$manager = VersionManager::instance();
|
||||
$result = self::callProtectedMethod($manager, 'getFileVersions', ['\Winter\\Tester']);
|
||||
|
||||
$this->assertCount(13, $result);
|
||||
$this->assertArrayHasKey('1.0.1', $result);
|
||||
$this->assertArrayHasKey('1.0.2', $result);
|
||||
$this->assertArrayHasKey('1.0.3', $result);
|
||||
$this->assertArrayHasKey('1.0.4', $result);
|
||||
$this->assertArrayHasKey('1.0.5', $result);
|
||||
$this->assertArrayHasKey('1.1.0', $result);
|
||||
$this->assertArrayHasKey('1.2.0', $result);
|
||||
$this->assertArrayHasKey('1.3.0', $result);
|
||||
$this->assertArrayHasKey('1.3.1', $result);
|
||||
$this->assertArrayHasKey('1.3.2', $result);
|
||||
$this->assertArrayHasKey('1.4.1', $result);
|
||||
$this->assertArrayHasKey('1.5.0', $result);
|
||||
$this->assertArrayHasKey('1.5.1', $result);
|
||||
|
||||
$sample = $result['1.0.1'];
|
||||
$this->assertEquals('Added some upgrade file and some "seeding"', $sample[0]);
|
||||
|
||||
$sample = $result['1.1.0'];
|
||||
$this->assertEquals('!!! Drop support for blog settings', $sample[0]);
|
||||
$this->assertEquals('drop_blog_settings_table.php', $sample[1]);
|
||||
|
||||
$sample = $result['1.2.0'];
|
||||
$this->assertEquals('!!! Security update - see: https://wintercms.com', $sample[0]);
|
||||
|
||||
$sample = $result['1.3.0'];
|
||||
$this->assertEquals('!!! We\'ve refactored major parts of this plugin. Please see the website for more information.', $sample);
|
||||
|
||||
$sample = $result['1.3.1'];
|
||||
$this->assertEquals('Minor bug fix Please see changelog', $sample[0]);
|
||||
$this->assertEquals('fix_database.php', $sample[1]);
|
||||
|
||||
$sample = $result['1.3.2'];
|
||||
$this->assertEquals('Added support for Translate plugin. Added some new languages.', $sample);
|
||||
|
||||
$sample = $result['1.5.0'];
|
||||
$this->assertEquals('!!! Another major update to fix several issues', $sample);
|
||||
|
||||
$sample = $result['1.5.1'];
|
||||
$this->assertEquals('Improved signature with the Test::method()', $sample[0]);
|
||||
$this->assertEquals('Translation updates.', $sample[1]);
|
||||
|
||||
/*
|
||||
* Test junk file
|
||||
*/
|
||||
$result = self::callProtectedMethod($manager, 'getFileVersions', ['\Winter\\Sample']);
|
||||
$this->assertCount(5, $result);
|
||||
$this->assertArrayHasKey('junk', $result);
|
||||
$this->assertArrayHasKey('1', $result);
|
||||
$this->assertArrayHasKey('1.0.*', $result);
|
||||
$this->assertArrayHasKey('1.0.x', $result);
|
||||
$this->assertArrayHasKey('10.3', $result);
|
||||
|
||||
$sample = array_shift($result);
|
||||
$comment = array_shift($sample);
|
||||
$this->assertEquals("JUNK JUNK JUNK", $comment);
|
||||
|
||||
/*
|
||||
* Test empty file
|
||||
*/
|
||||
$result = self::callProtectedMethod($manager, 'getFileVersions', ['\Winter\\NoUpdates']);
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
public function testGetNewFileVersions()
|
||||
{
|
||||
$manager = VersionManager::instance();
|
||||
$result = self::callProtectedMethod($manager, 'getNewFileVersions', ['\Winter\\Tester', '1.0.3']);
|
||||
|
||||
$this->assertCount(10, $result);
|
||||
$this->assertArrayHasKey('1.0.4', $result);
|
||||
$this->assertArrayHasKey('1.0.5', $result);
|
||||
$this->assertArrayHasKey('1.1.0', $result);
|
||||
$this->assertArrayHasKey('1.2.0', $result);
|
||||
$this->assertArrayHasKey('1.3.0', $result);
|
||||
$this->assertArrayHasKey('1.3.1', $result);
|
||||
$this->assertArrayHasKey('1.3.2', $result);
|
||||
$this->assertArrayHasKey('1.4.1', $result);
|
||||
$this->assertArrayHasKey('1.5.0', $result);
|
||||
$this->assertArrayHasKey('1.5.1', $result);
|
||||
|
||||
/*
|
||||
* When at version 0, should return everything
|
||||
*/
|
||||
$manager = VersionManager::instance();
|
||||
$result = self::callProtectedMethod($manager, 'getNewFileVersions', ['\Winter\\Tester']);
|
||||
|
||||
$this->assertCount(13, $result);
|
||||
$this->assertArrayHasKey('1.0.1', $result);
|
||||
$this->assertArrayHasKey('1.0.2', $result);
|
||||
$this->assertArrayHasKey('1.0.3', $result);
|
||||
$this->assertArrayHasKey('1.0.4', $result);
|
||||
$this->assertArrayHasKey('1.0.5', $result);
|
||||
$this->assertArrayHasKey('1.1.0', $result);
|
||||
$this->assertArrayHasKey('1.2.0', $result);
|
||||
$this->assertArrayHasKey('1.3.0', $result);
|
||||
$this->assertArrayHasKey('1.3.1', $result);
|
||||
$this->assertArrayHasKey('1.3.2', $result);
|
||||
$this->assertArrayHasKey('1.4.1', $result);
|
||||
$this->assertArrayHasKey('1.5.0', $result);
|
||||
$this->assertArrayHasKey('1.5.1', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider versionInfoProvider
|
||||
*
|
||||
* @param $versionInfo
|
||||
* @param $expectedComments
|
||||
* @param $expectedScripts
|
||||
*/
|
||||
public function testExtractScriptsAndComments($versionInfo, $expectedComments, $expectedScripts)
|
||||
{
|
||||
$manager = VersionManager::instance();
|
||||
list($comments, $scripts) = self::callProtectedMethod($manager, 'extractScriptsAndComments', [$versionInfo]);
|
||||
|
||||
$this->assertIsArray($comments);
|
||||
$this->assertIsArray($scripts);
|
||||
|
||||
$this->assertEquals($expectedComments, $comments);
|
||||
$this->assertEquals($expectedScripts, $scripts);
|
||||
}
|
||||
|
||||
public function versionInfoProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'A single update comment string',
|
||||
[
|
||||
'A single update comment string'
|
||||
],
|
||||
[]
|
||||
],
|
||||
[
|
||||
[
|
||||
'A classic update comment string followed by script',
|
||||
'update_script.php'
|
||||
],
|
||||
[
|
||||
'A classic update comment string followed by script'
|
||||
],
|
||||
[
|
||||
'update_script.php'
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
'scripts_can_go_first.php',
|
||||
'An update comment string after the script',
|
||||
],
|
||||
[
|
||||
'An update comment string after the script'
|
||||
],
|
||||
[
|
||||
'scripts_can_go_first.php'
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
'scripts_can_go_first.php',
|
||||
'An update comment string after the script',
|
||||
'scripts_can_go_anywhere.php',
|
||||
],
|
||||
[
|
||||
'An update comment string after the script'
|
||||
],
|
||||
[
|
||||
'scripts_can_go_first.php',
|
||||
'scripts_can_go_anywhere.php'
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
'scripts_can_go_first.php',
|
||||
'The first update comment',
|
||||
'scripts_can_go_anywhere.php',
|
||||
'The second update comment',
|
||||
],
|
||||
[
|
||||
'The first update comment',
|
||||
'The second update comment'
|
||||
],
|
||||
[
|
||||
'scripts_can_go_first.php',
|
||||
'scripts_can_go_anywhere.php'
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
'file.name.with.dots.php',
|
||||
'The first update comment',
|
||||
'1.0.2.scripts_can_go_anywhere.php',
|
||||
'The second update comment',
|
||||
],
|
||||
[
|
||||
'The first update comment',
|
||||
'The second update comment'
|
||||
],
|
||||
[
|
||||
'file.name.with.dots.php',
|
||||
'1.0.2.scripts_can_go_anywhere.php'
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
'subdirectory/file.name.with.dots.php',
|
||||
'The first update comment',
|
||||
'subdirectory\1.0.2.scripts_can_go_anywhere.php',
|
||||
'The second update comment',
|
||||
],
|
||||
[
|
||||
'The first update comment',
|
||||
'The second update comment'
|
||||
],
|
||||
[
|
||||
'subdirectory/file.name.with.dots.php',
|
||||
'subdirectory\1.0.2.scripts_can_go_anywhere.php'
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
232
modules/system/tests/classes/asset/BundleManagerTest.php
Normal file
232
modules/system/tests/classes/asset/BundleManagerTest.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes\Asset;
|
||||
|
||||
use System\Classes\Asset\BundleManager;
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
|
||||
class BundleManagerTest extends TestCase
|
||||
{
|
||||
protected BundleManager $bundleManager;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
// Reset the bundle manager
|
||||
BundleManager::forgetInstance();
|
||||
// Bind the instance for convenience
|
||||
$this->bundleManager = BundleManager::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the registerBundles & registerBundle functions correctly allow the user to append bundles
|
||||
*/
|
||||
public function testRegisterBundles(): void
|
||||
{
|
||||
$defaultBundles = $this->bundleManager->getBundles();
|
||||
$this->bundleManager->registerBundles([
|
||||
'winter1js' => [
|
||||
'winter1js' => 'v1.0.0'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->bundleManager->registerBundle('winter2js', [
|
||||
'winter2js' => 'v1.0.0'
|
||||
]);
|
||||
|
||||
$bundles = $this->bundleManager->getBundles();
|
||||
$this->assertCount(count($defaultBundles) + 2, $bundles);
|
||||
$this->assertContains('winter1js', $bundles);
|
||||
$this->assertContains('winter2js', $bundles);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that defining new bundles does not affect the default bundles
|
||||
*/
|
||||
public function testRegisterBundlesDoesNotBreakDefaults(): void
|
||||
{
|
||||
// Get the default bundles so we can validate them existing later
|
||||
$defaultBundles = $this->bundleManager->getBundles();
|
||||
// Flush the instance as if we have just booted
|
||||
BundleManager::forgetInstance();
|
||||
$this->bundleManager = BundleManager::instance();
|
||||
// Register a new bundle
|
||||
$this->bundleManager->registerBundles([
|
||||
'winterjs' => [
|
||||
'winterjs' => 'v1.0.0'
|
||||
]
|
||||
]);
|
||||
// Grab the current bundles
|
||||
$bundles = $this->bundleManager->getBundles();
|
||||
// Validate that all default bundles have been registered when adding a new bundle
|
||||
foreach ($defaultBundles as $defaultBundle) {
|
||||
$this->assertContains($defaultBundle, $bundles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the forcing a package to a new version
|
||||
*/
|
||||
public function testRegisterBundlesOverrideDefault(): void
|
||||
{
|
||||
$tailwindPackages = $this->bundleManager->getBundlePackages('tailwind', 'mix');
|
||||
|
||||
$this->bundleManager->registerBundle('tailwind', [
|
||||
'tailwindcss' => 'dev-999.999.999',
|
||||
'@tailwindcss/forms' => 'dev-999.999.999',
|
||||
'@tailwindcss/typography' => 'dev-999.999.999',
|
||||
]);
|
||||
|
||||
$updatedTailwindPackages = $this->bundleManager->getBundlePackages('tailwind', 'mix');
|
||||
|
||||
foreach ($tailwindPackages as $tailwindPackage => $version) {
|
||||
$this->assertArrayHasKey($tailwindPackage, $updatedTailwindPackages);
|
||||
$this->assertNotEquals($updatedTailwindPackages[$tailwindPackage], $version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting all supported bundles
|
||||
*/
|
||||
public function testGetBundles(): void
|
||||
{
|
||||
$bundles = $this->bundleManager->getBundles();
|
||||
|
||||
$this->assertIsArray($bundles);
|
||||
$count = count($bundles);
|
||||
|
||||
$this->bundleManager->registerBundle('test', [
|
||||
'a' => 'v0.1.2',
|
||||
]);
|
||||
|
||||
$this->assertCount($count + 1, $this->bundleManager->getBundles());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting bundle packages works and allows for config overloading
|
||||
*/
|
||||
public function testGetBundlePackages(): void
|
||||
{
|
||||
// Test that getting a default returns an array and validate one of the packages
|
||||
$packages = $this->bundleManager->getBundlePackages('tailwind', 'mix');
|
||||
$this->assertIsArray($packages);
|
||||
$this->assertArrayHasKey('tailwindcss', $packages);
|
||||
|
||||
// Test that getting a package with compiler dependant packages does not return the package for invalid compiler
|
||||
$packages = $this->bundleManager->getBundlePackages('vue', 'mix');
|
||||
$this->assertIsArray($packages);
|
||||
$this->assertArrayNotHasKey('@vitejs/plugin-vue', $packages);
|
||||
|
||||
// Test that getting a package with compiler dependant packages does return the package for a valid compiler
|
||||
$packages = $this->bundleManager->getBundlePackages('vue', 'vite');
|
||||
$this->assertIsArray($packages);
|
||||
$this->assertArrayHasKey('@vitejs/plugin-vue', $packages);
|
||||
|
||||
// Validate that `testing` does not exist
|
||||
$packages = $this->bundleManager->getBundlePackages('testing', 'vite');
|
||||
$this->assertIsArray($packages);
|
||||
$this->assertEmpty($packages);
|
||||
|
||||
$this->bundleManager->registerBundle('testing', [
|
||||
'a' => 'v0.1.2',
|
||||
'mix' => [
|
||||
'b' => 'v0.1.3',
|
||||
]
|
||||
]);
|
||||
|
||||
// Validate the testing bundle works with compiler dependent packages
|
||||
$packages = $this->bundleManager->getBundlePackages('testing', 'vite');
|
||||
$this->assertIsArray($packages);
|
||||
$this->assertCount(1, $packages);
|
||||
|
||||
$packages = $this->bundleManager->getBundlePackages('testing', 'mix');
|
||||
$this->assertIsArray($packages);
|
||||
$this->assertCount(2, $packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the AssetBundles setup handlers functionality
|
||||
*/
|
||||
public function testSetupHandlers(): void
|
||||
{
|
||||
// Test that the default handler is accessible
|
||||
$handler = $this->bundleManager->getSetupHandler('tailwind');
|
||||
$this->assertIsCallable($handler);
|
||||
|
||||
// Add a new handler
|
||||
$this->bundleManager->registerSetupHandler('testing', fn () => true);
|
||||
$handler = $this->bundleManager->getSetupHandler('testing');
|
||||
$this->assertIsCallable($handler);
|
||||
|
||||
// Validate that handler returned is ours
|
||||
$this->assertTrue($handler());
|
||||
|
||||
// Override the handler
|
||||
$this->bundleManager->registerSetupHandler('testing', fn () => false);
|
||||
$handler = $this->bundleManager->getSetupHandler('testing');
|
||||
$this->assertIsCallable($handler);
|
||||
$this->assertFalse($handler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the AssetBundles scaffold handlers functionality
|
||||
*/
|
||||
public function testScaffoldHandlers(): void
|
||||
{
|
||||
// Test that the default handler is accessible
|
||||
$handler = $this->bundleManager->getScaffoldHandler('tailwind');
|
||||
$this->assertIsCallable($handler);
|
||||
|
||||
// Add a new handler
|
||||
$this->bundleManager->registerScaffoldHandler('testing', fn () => true);
|
||||
$handler = $this->bundleManager->getScaffoldHandler('testing');
|
||||
$this->assertIsCallable($handler);
|
||||
|
||||
// Validate that handler returned is ours
|
||||
$this->assertTrue($handler());
|
||||
|
||||
// Override the handler
|
||||
$this->bundleManager->registerScaffoldHandler('testing', fn () => false);
|
||||
$handler = $this->bundleManager->getScaffoldHandler('testing');
|
||||
$this->assertIsCallable($handler);
|
||||
$this->assertFalse($handler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that when registered a callable is added to the `callbacks` array within the BundleManager
|
||||
*/
|
||||
public function testRegisterCallback(): void
|
||||
{
|
||||
$this->expectExceptionMessage('callback registered');
|
||||
$this->bundleManager->registerCallback(function (BundleManager $manager) {
|
||||
throw new \RuntimeException('callback registered');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that calling registerBundles within registerCallback functions correctly
|
||||
*/
|
||||
public function testLoadRegisteredBundles(): void
|
||||
{
|
||||
$this->bundleManager->registerCallback(function (BundleManager $manager) {
|
||||
$manager->registerBundles([
|
||||
'winter-test-js' => [
|
||||
'test-package' => 'v0.1.2',
|
||||
'vite' => [
|
||||
'vite-package' => 'v0.1.3',
|
||||
]
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
$this->assertContains('winter-test-js', $this->bundleManager->getBundles());
|
||||
|
||||
$bundle = $this->bundleManager->getBundlePackages('winter-test-js', 'mix');
|
||||
$this->assertArrayHasKey('test-package', $bundle);
|
||||
$this->assertArrayNotHasKey('vite-package', $bundle);
|
||||
|
||||
$bundle = $this->bundleManager->getBundlePackages('winter-test-js', 'vite');
|
||||
$this->assertArrayHasKey('test-package', $bundle);
|
||||
$this->assertArrayHasKey('vite-package', $bundle);
|
||||
}
|
||||
}
|
||||
469
modules/system/tests/classes/asset/PackageJsonTest.php
Normal file
469
modules/system/tests/classes/asset/PackageJsonTest.php
Normal file
@@ -0,0 +1,469 @@
|
||||
<?php
|
||||
|
||||
namespace System\Tests\Classes\Asset;
|
||||
|
||||
use System\Classes\Asset\PackageJson;
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
|
||||
class PackageJsonTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Default test file fixture
|
||||
*/
|
||||
protected string $testFile;
|
||||
|
||||
/**
|
||||
* Bind the default test file fixture
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->testFile = __DIR__ . '/../../fixtures/npm/package-test.json';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading a package.json file from path
|
||||
*/
|
||||
public function testLoadFile(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$contents = $packageJson->getContents();
|
||||
|
||||
$this->assertArrayHasKey('workspaces', $contents);
|
||||
$this->assertArrayHasKey('packages', $contents['workspaces']);
|
||||
$this->assertIsArray($contents['workspaces']['packages']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading a corrupted package.json file correctly errors
|
||||
*/
|
||||
public function testLoadCorruptFile(): void
|
||||
{
|
||||
$this->expectException(\JsonException::class);
|
||||
new PackageJson(__DIR__ . '/../../fixtures/npm/package-corrupt.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating an instance with non-existing file
|
||||
*/
|
||||
public function testNewFile(): void
|
||||
{
|
||||
$packageJson = new PackageJson(__DIR__ . '/../fixtures/npm/package-test-new.json');
|
||||
$contents = $packageJson->getContents();
|
||||
$this->assertIsArray($contents);
|
||||
$this->assertCount(0, $contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating an instance without a path
|
||||
*/
|
||||
public function testMemoryInstance(): void
|
||||
{
|
||||
$packageJson = new PackageJson();
|
||||
$contents = $packageJson->getContents();
|
||||
$this->assertIsArray($contents);
|
||||
$this->assertCount(0, $contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting and getting the name property
|
||||
*/
|
||||
public function testNameMethods(): void
|
||||
{
|
||||
$packageJson = new PackageJson();
|
||||
$contents = $packageJson->getContents();
|
||||
$this->assertIsArray($contents);
|
||||
$this->assertCount(0, $contents);
|
||||
|
||||
$packageJson->setName('example-name');
|
||||
|
||||
$this->assertEquals('example-name', $packageJson->getName());
|
||||
|
||||
$contents = $packageJson->getContents();
|
||||
$this->assertIsArray($contents);
|
||||
$this->assertCount(1, $contents);
|
||||
$this->assertArrayHasKey('name', $contents);
|
||||
$this->assertEquals('example-name', $contents['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting the path of the current file
|
||||
*/
|
||||
public function testGetPath(): void
|
||||
{
|
||||
$packageJson = new PackageJson(__DIR__ . '/package.json');
|
||||
$path = $packageJson->getPath();
|
||||
$this->assertIsString($path);
|
||||
$this->assertEquals(__DIR__ . '/package.json', $path);
|
||||
|
||||
$packageJson = new PackageJson();
|
||||
$path = $packageJson->getPath();
|
||||
$this->assertNull($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validating the name on set
|
||||
*/
|
||||
public function testNameValidation(): void
|
||||
{
|
||||
$packageJson = new PackageJson();
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('Test');
|
||||
}, \InvalidArgumentException::class, 'Package names must be lower case');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('.test');
|
||||
}, \InvalidArgumentException::class, 'Package names must not start with . or _');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('_test');
|
||||
}, \InvalidArgumentException::class, 'Package names must not start with . or _');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('te~st');
|
||||
}, \InvalidArgumentException::class, 'Package names must not include special characters');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('te*st');
|
||||
}, \InvalidArgumentException::class, 'Package names must not include special characters');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('test!');
|
||||
}, \InvalidArgumentException::class, 'Package names must not include special characters');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName(sprintf('te%sst', str_repeat('s', 214)));
|
||||
}, \InvalidArgumentException::class, 'Package names must not be longer than 214 characters');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName('test ');
|
||||
}, \InvalidArgumentException::class, 'Package names must not include whitespace');
|
||||
|
||||
$this->assertThrows(function () use ($packageJson) {
|
||||
$packageJson->setName(' test');
|
||||
}, \InvalidArgumentException::class, 'Package names must not include whitespace');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking package workspace exists
|
||||
*/
|
||||
public function testHasWorkspace(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertTrue($packageJson->hasWorkspace('themes/demo'));
|
||||
$this->assertFalse($packageJson->hasWorkspace('themes/test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding workspace package
|
||||
*/
|
||||
public function testAddWorkspace(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertFalse($packageJson->hasWorkspace('themes/test'));
|
||||
$packageJson->addWorkspace('themes/test');
|
||||
$this->assertTrue($packageJson->hasWorkspace('themes/test'));
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
$this->assertFalse($packageJson->hasWorkspace('themes/test'));
|
||||
$packageJson->addWorkspace('themes/test');
|
||||
$this->assertTrue($packageJson->hasWorkspace('themes/test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing workspace package
|
||||
*/
|
||||
public function testRemoveWorkspace(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertTrue($packageJson->hasWorkspace('themes/demo'));
|
||||
$packageJson->removeWorkspace('themes/demo');
|
||||
$this->assertFalse($packageJson->hasWorkspace('themes/demo'));
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->removeWorkspace('themes/demo');
|
||||
$this->assertFalse($packageJson->hasWorkspace('themes/demo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when adding a workspace package, it removes the package from ignored workspace packages
|
||||
*/
|
||||
public function testAddWorkspaceRemovesIgnoredPackage(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertFalse($packageJson->hasWorkspace('modules/backend'));
|
||||
$this->assertTrue($packageJson->hasIgnoredPackage('modules/backend'));
|
||||
|
||||
$packageJson->addWorkspace('modules/backend');
|
||||
|
||||
$this->assertTrue($packageJson->hasWorkspace('modules/backend'));
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('modules/backend'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking ignore package workspace exists
|
||||
*/
|
||||
public function testHasIgnoredPackage(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertTrue($packageJson->hasIgnoredPackage('modules/backend'));
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('modules/test'));
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('modules/backend'));
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('modules/test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding ignore workspace package
|
||||
*/
|
||||
public function testAddIgnoredPackage(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('themes/example'));
|
||||
$packageJson->addIgnoredPackage('themes/example');
|
||||
$this->assertTrue($packageJson->hasIgnoredPackage('themes/example'));
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->addIgnoredPackage('themes/example');
|
||||
$this->assertTrue($packageJson->hasIgnoredPackage('themes/example'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing ignore workspace package
|
||||
*/
|
||||
public function testRemoveIgnoredPackage(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertTrue($packageJson->hasIgnoredPackage('modules/system'));
|
||||
$packageJson->removeIgnoredPackage('modules/system');
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('modules/system'));
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->removeIgnoredPackage('modules/system');
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('modules/system'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when adding an ignore workspace package, it removes the package from workspace packages
|
||||
*/
|
||||
public function testAddIgnoredPackageRemovesWorkspace(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
$this->assertTrue($packageJson->hasWorkspace('themes/demo'));
|
||||
$this->assertFalse($packageJson->hasIgnoredPackage('themes/demo'));
|
||||
|
||||
$packageJson->addIgnoredPackage('themes/demo');
|
||||
|
||||
$this->assertFalse($packageJson->hasWorkspace('themes/demo'));
|
||||
$this->assertTrue($packageJson->hasIgnoredPackage('themes/demo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking if package.json has deps
|
||||
*/
|
||||
public function testHasDependency(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
// Check that deps exist
|
||||
$this->assertTrue($packageJson->hasDependency('test'));
|
||||
$this->assertTrue($packageJson->hasDependency('test-dev'));
|
||||
// Check that deps don't exist
|
||||
$this->assertFalse($packageJson->hasWorkspace('test-dev2'));
|
||||
$this->assertFalse($packageJson->hasWorkspace('testx'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding dependencies, when overwriting check that package is moved from devDeps to deps or revsersed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testAddDependency(): void
|
||||
{
|
||||
// Test adding packages
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
$packageJson->addDependency('winter', '4.0.1', dev: false)
|
||||
->addDependency('winter-dev', '4.0.1', dev: true);
|
||||
|
||||
$this->assertArrayNotHasKey('winter', $packageJson->getContents()['devDependencies']);
|
||||
$this->assertArrayHasKey('winter', $packageJson->getContents()['dependencies']);
|
||||
$this->assertArrayNotHasKey('winter-dev', $packageJson->getContents()['dependencies']);
|
||||
$this->assertArrayHasKey('winter-dev', $packageJson->getContents()['devDependencies']);
|
||||
|
||||
// Test adding packages with overwrites
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
// Should do nothing as test is already in deps not dev deps
|
||||
$packageJson->addDependency('test', '6.0.1', dev: true, overwrite: false);
|
||||
|
||||
$this->assertArrayHasKey('test', $packageJson->getContents()['dependencies']);
|
||||
$this->assertArrayNotHasKey('test', $packageJson->getContents()['devDependencies']);
|
||||
$this->assertEquals('^1.0.0', $packageJson->getContents()['dependencies']['test']);
|
||||
|
||||
// Should move package from dev deps to deps with new version
|
||||
$packageJson->addDependency('test', '6.0.1', dev: true, overwrite: true);
|
||||
|
||||
$this->assertArrayNotHasKey('test', $packageJson->getContents()['dependencies']);
|
||||
$this->assertArrayHasKey('test', $packageJson->getContents()['devDependencies']);
|
||||
$this->assertEquals('6.0.1', $packageJson->getContents()['devDependencies']['test']);
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
// Add a non-dev dependency
|
||||
$packageJson->addDependency('winter', '4.0.1', dev: false);
|
||||
// Add a dev dependency
|
||||
$packageJson->addDependency('winter-dev', '4.0.1', dev: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing dependencies
|
||||
*/
|
||||
public function testRemoveDependency(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
// Check package removed from dev deps
|
||||
$this->assertArrayHasKey('test-dev', $packageJson->getContents()['devDependencies'] ?? []);
|
||||
$packageJson->removeDependency('test-dev');
|
||||
$this->assertArrayNotHasKey('test-dev', $packageJson->getContents()['devDependencies'] ?? []);
|
||||
|
||||
// Check package removed from deps
|
||||
$this->assertArrayHasKey('test', $packageJson->getContents()['dependencies'] ?? []);
|
||||
$packageJson->removeDependency('test');
|
||||
$this->assertArrayNotHasKey('test', $packageJson->getContents()['dependencies'] ?? []);
|
||||
|
||||
// Create blank source
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->removeDependency('test-dev');
|
||||
$this->assertArrayNotHasKey('test-dev', $packageJson->getContents()['devDependencies'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking if a script exists in a package.json
|
||||
*/
|
||||
public function testHasScript(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
$this->assertTrue($packageJson->hasScript('foo'));
|
||||
$this->assertTrue($packageJson->hasScript('example'));
|
||||
$this->assertTrue($packageJson->hasScript('test'));
|
||||
|
||||
$this->assertFalse($packageJson->hasScript('bar'));
|
||||
$this->assertFalse($packageJson->hasScript('winter'));
|
||||
$this->assertFalse($packageJson->hasScript('testing'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting the value of a script by name
|
||||
*/
|
||||
public function testGetScript(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
$this->assertEquals('bar ./test', $packageJson->getScript('foo'));
|
||||
$this->assertEquals('example test', $packageJson->getScript('example'));
|
||||
$this->assertEquals('testing', $packageJson->getScript('test'));
|
||||
|
||||
$packageJson = new PackageJson();
|
||||
$this->assertNull($packageJson->getScript('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting the value of a script by name
|
||||
*/
|
||||
public function testAddScript(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
$packageJson->addScript('winter', 'winter ./testing');
|
||||
|
||||
$this->assertTrue($packageJson->hasScript('winter'));
|
||||
$this->assertEquals('winter ./testing', $packageJson->getScript('winter'));
|
||||
|
||||
$contents = $packageJson->getContents();
|
||||
|
||||
$this->assertTrue(isset($contents['scripts']['winter']));
|
||||
$this->assertEquals('winter ./testing', $contents['scripts']['winter']);
|
||||
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->addScript('winter', 'winter ./testing');
|
||||
$this->assertTrue($packageJson->hasScript('winter'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing scripts from package.json
|
||||
*/
|
||||
public function testRemoveScript(): void
|
||||
{
|
||||
$packageJson = new PackageJson($this->testFile);
|
||||
|
||||
$this->assertTrue($packageJson->hasScript('foo'));
|
||||
$packageJson->removeScript('foo');
|
||||
$this->assertFalse($packageJson->hasScript('foo'));
|
||||
|
||||
$this->assertTrue($packageJson->hasScript('example'));
|
||||
$packageJson->removeScript('example');
|
||||
$this->assertFalse($packageJson->hasScript('example'));
|
||||
|
||||
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->removeScript('foo');
|
||||
$this->assertFalse($packageJson->hasScript('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test saving, when saving with a file path set on init and passing a file path on save. Fails when no path given
|
||||
*/
|
||||
public function testSave(): void
|
||||
{
|
||||
$srcFile = $this->testFile;
|
||||
$backupFile = __DIR__ . '/../../fixtures/npm/package-test.json.back';
|
||||
|
||||
// Backup config file
|
||||
copy($srcFile, $backupFile);
|
||||
|
||||
// Make a change and save the file, overwriting
|
||||
$packageJson = new PackageJson($srcFile);
|
||||
$this->assertNull($packageJson->getName());
|
||||
$packageJson->setName('testing');
|
||||
$packageJson->save();
|
||||
|
||||
// Validate overwrite worked
|
||||
$packageJson = new PackageJson($srcFile);
|
||||
$this->assertEquals('testing', $packageJson->getName());
|
||||
|
||||
// Restore the config file
|
||||
copy($backupFile, $srcFile);
|
||||
// Remove backup file
|
||||
unlink($backupFile);
|
||||
|
||||
// Test saving file to new path
|
||||
$testFile = __DIR__ . '/../../fixtures/npm/package-test.json.test';
|
||||
$packageJson = new PackageJson($srcFile);
|
||||
$this->assertNull($packageJson->getName());
|
||||
$packageJson->setName('testing');
|
||||
$packageJson->save($testFile);
|
||||
|
||||
// Validate new file path exists and contains change
|
||||
$packageJson = new PackageJson($testFile);
|
||||
$this->assertEquals('testing', $packageJson->getName());
|
||||
|
||||
// Remove test file
|
||||
unlink($testFile);
|
||||
|
||||
// Validate that a file save with no path throws an error
|
||||
$this->assertThrows(function () {
|
||||
$packageJson = new PackageJson();
|
||||
$packageJson->setName('should-fail');
|
||||
$packageJson->save();
|
||||
}, \RuntimeException::class, 'Unable to save, no path given');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user