167 lines
6.1 KiB
PHP
167 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace Modules\Stylist\Tests\Feature;
|
|
|
|
use Illuminate\Filesystem\Filesystem;
|
|
use Modules\Stylist\Theme\Exceptions\InvalidThemeManifestException;
|
|
use Modules\Stylist\Theme\Loader;
|
|
use Tests\TestCase;
|
|
|
|
final class ThemeManifestTest extends TestCase
|
|
{
|
|
private string $themesPath;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->themesPath = storage_path('framework/testing/stylist-themes-'.uniqid());
|
|
mkdir($this->themesPath, 0777, true);
|
|
config(['stylist.themes_path' => $this->themesPath]);
|
|
app(Loader::class)->flush();
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
app(Filesystem::class)->deleteDirectory($this->themesPath);
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function test_bundled_default_themes_have_valid_manifests(): void
|
|
{
|
|
foreach (['backend', 'frontend'] as $key) {
|
|
$theme = app(Loader::class)->fromPath(resource_path("themes/{$key}"));
|
|
|
|
$this->assertSame($key, $theme->key());
|
|
}
|
|
}
|
|
|
|
public function test_manifest_is_normalised(): void
|
|
{
|
|
$path = $this->themesPath.'/sample';
|
|
mkdir($path.'/css', 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode(['name' => 'Sample']));
|
|
file_put_contents($path.'/css/theme.css', 'body{}');
|
|
|
|
$theme = app(Loader::class)->fromPath($path);
|
|
|
|
$this->assertSame('sample', $theme->key());
|
|
$this->assertSame('css/theme.css', $theme->stylesheet());
|
|
$this->assertSame('js/app.js', $theme->script());
|
|
$this->assertSame('home', $theme->entryView());
|
|
}
|
|
|
|
public function test_unsafe_manifest_path_is_rejected(): void
|
|
{
|
|
$path = $this->themesPath.'/unsafe';
|
|
mkdir($path, 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode([
|
|
'name' => 'Unsafe',
|
|
'stylesheet' => '../secret.css',
|
|
]));
|
|
|
|
$this->expectException(InvalidThemeManifestException::class);
|
|
app(Loader::class)->fromPath($path);
|
|
}
|
|
|
|
public function test_phase_one_manifest_optional_capabilities_are_normalised(): void
|
|
{
|
|
$path = $this->themesPath.'/design-gallery';
|
|
mkdir($path.'/css', 0777, true);
|
|
mkdir($path.'/js', 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode([
|
|
'key' => 'design-gallery',
|
|
'name' => 'Design Gallery',
|
|
'stylesheet' => 'css/app.css',
|
|
'script' => 'js/app.js',
|
|
'appearances' => ['light', 'dark', 'system'],
|
|
'accents' => ['blue', 'emerald', 'neutral'],
|
|
'default_appearance' => 'system',
|
|
'default_accent' => 'neutral',
|
|
// Phase 1 incorrectly duplicated accents here; Stylist must not
|
|
// reject the whole theme because of that compact legacy list.
|
|
'variants' => ['blue', 'emerald', 'neutral'],
|
|
]));
|
|
file_put_contents($path.'/css/app.css', 'body{}');
|
|
file_put_contents($path.'/js/app.js', '');
|
|
|
|
$theme = app(Loader::class)->fromPath($path);
|
|
|
|
$this->assertSame(['light', 'dark', 'system'], array_keys($theme->appearances()));
|
|
$this->assertSame(['blue', 'emerald', 'neutral'], array_keys($theme->accents()));
|
|
$this->assertSame([], $theme->variants());
|
|
$this->assertSame('system', $theme->defaultAppearance());
|
|
$this->assertSame('neutral', $theme->defaultAccent());
|
|
}
|
|
|
|
public function test_legacy_manifest_does_not_gain_appearance_or_accent_capabilities(): void
|
|
{
|
|
$path = $this->themesPath.'/legacy';
|
|
mkdir($path.'/css', 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode(['name' => 'Legacy']));
|
|
file_put_contents($path.'/css/theme.css', 'body{}');
|
|
|
|
$theme = app(Loader::class)->fromPath($path);
|
|
|
|
$this->assertSame([], $theme->appearances());
|
|
$this->assertSame([], $theme->accents());
|
|
$this->assertSame('', $theme->defaultAppearance());
|
|
$this->assertSame('', $theme->defaultAccent());
|
|
}
|
|
|
|
public function test_defaults_must_reference_declared_optional_capabilities(): void
|
|
{
|
|
$path = $this->themesPath.'/invalid-default';
|
|
mkdir($path, 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode([
|
|
'name' => 'Invalid Default',
|
|
'appearances' => ['light', 'dark'],
|
|
'default_appearance' => 'system',
|
|
]));
|
|
|
|
$this->expectException(InvalidThemeManifestException::class);
|
|
app(Loader::class)->fromPath($path);
|
|
}
|
|
|
|
public function test_manifest_version_and_capabilities_are_normalised(): void
|
|
{
|
|
$path = $this->themesPath.'/capable';
|
|
mkdir($path.'/css', 0777, true);
|
|
mkdir($path.'/js', 0777, true);
|
|
mkdir($path.'/views', 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode([
|
|
'manifest_version' => 1,
|
|
'name' => 'Capable',
|
|
'appearances' => ['light', 'dark'],
|
|
'accents' => ['blue'],
|
|
'capabilities' => ['marketplace' => true],
|
|
]));
|
|
file_put_contents($path.'/css/theme.css', 'body{}');
|
|
file_put_contents($path.'/js/app.js', '');
|
|
file_put_contents($path.'/views/home.blade.php', '<div>Capable</div>');
|
|
|
|
$theme = app(Loader::class)->fromPath($path);
|
|
|
|
$this->assertSame(1, $theme->manifestVersion());
|
|
$this->assertTrue($theme->hasCapability('appearances'));
|
|
$this->assertTrue($theme->hasCapability('accents'));
|
|
$this->assertTrue($theme->hasCapability('marketplace'));
|
|
$this->assertFalse($theme->hasCapability('preview'));
|
|
}
|
|
|
|
public function test_unknown_manifest_fields_are_preserved_without_preventing_loading(): void
|
|
{
|
|
$path = $this->themesPath.'/future';
|
|
mkdir($path.'/css', 0777, true);
|
|
file_put_contents($path.'/theme.json', json_encode([
|
|
'name' => 'Future',
|
|
'future_builder_metadata' => ['anything' => true],
|
|
]));
|
|
file_put_contents($path.'/css/theme.css', 'body{}');
|
|
|
|
$theme = app(Loader::class)->fromPath($path);
|
|
|
|
$this->assertSame(['anything' => true], $theme->meta()['future_builder_metadata']);
|
|
}
|
|
}
|