137 lines
4.7 KiB
PHP
137 lines
4.7 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace Tests\Feature\Admin;
|
||
|
|
|
||
|
|
use App\Models\Role;
|
||
|
|
use App\Models\User;
|
||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
|
use Illuminate\Support\Facades\File;
|
||
|
|
use Tests\TestCase;
|
||
|
|
|
||
|
|
class ThemeEditorTest extends TestCase
|
||
|
|
{
|
||
|
|
use RefreshDatabase;
|
||
|
|
|
||
|
|
protected User $admin;
|
||
|
|
protected string $themePath;
|
||
|
|
protected string $themeName = 'test-editor-theme';
|
||
|
|
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
parent::setUp();
|
||
|
|
$this->seed(\Database\Seeders\PermissionSeeder::class);
|
||
|
|
$this->seed(\Database\Seeders\RoleSeeder::class);
|
||
|
|
|
||
|
|
$this->admin = User::factory()->create();
|
||
|
|
$adminRole = Role::where('slug', 'admin')->first();
|
||
|
|
|
||
|
|
// Ensure the admin role has the new permission
|
||
|
|
$editThemesPermission = \App\Models\Permission::where('slug', 'edit-themes')->first();
|
||
|
|
$adminRole->permissions()->syncWithoutDetaching([$editThemesPermission->id]);
|
||
|
|
|
||
|
|
$this->admin->roles()->attach($adminRole);
|
||
|
|
|
||
|
|
$this->themePath = base_path('themes/' . $this->themeName);
|
||
|
|
if (!File::exists($this->themePath)) {
|
||
|
|
File::makeDirectory($this->themePath, 0755, true);
|
||
|
|
File::put($this->themePath . '/theme.md', "Title: Test Editor Theme\nAuthor: Junie");
|
||
|
|
File::put($this->themePath . '/index.blade.php', "<h1>Hello</h1>");
|
||
|
|
File::makeDirectory($this->themePath . '/assets/css', 0755, true);
|
||
|
|
File::put($this->themePath . '/assets/css/style.css', "body { color: red; }");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function tearDown(): void
|
||
|
|
{
|
||
|
|
if (File::exists($this->themePath)) {
|
||
|
|
File::deleteDirectory($this->themePath);
|
||
|
|
}
|
||
|
|
parent::tearDown();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_admin_can_view_theme_editor_index()
|
||
|
|
{
|
||
|
|
$response = $this->actingAs($this->admin)->get('/loom/themes/editor');
|
||
|
|
$response->assertStatus(200);
|
||
|
|
$response->assertViewIs('admin.themes.editor');
|
||
|
|
$response->assertSee('test-editor-theme');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_admin_can_get_file_tree()
|
||
|
|
{
|
||
|
|
$response = $this->actingAs($this->admin)->getJson('/loom/themes/editor/tree?theme=' . $this->themeName);
|
||
|
|
$response->assertStatus(200);
|
||
|
|
|
||
|
|
$data = $response->json();
|
||
|
|
$this->assertIsArray($data);
|
||
|
|
|
||
|
|
// Find index.blade.php
|
||
|
|
$indexFile = collect($data)->firstWhere('name', 'index.blade.php');
|
||
|
|
$this->assertNotNull($indexFile);
|
||
|
|
$this->assertEquals('file', $indexFile['type']);
|
||
|
|
|
||
|
|
// Find assets directory
|
||
|
|
$assetsDir = collect($data)->firstWhere('name', 'assets');
|
||
|
|
$this->assertNotNull($assetsDir);
|
||
|
|
$this->assertEquals('directory', $assetsDir['type']);
|
||
|
|
$this->assertNotEmpty($assetsDir['children']);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_admin_can_read_file_content()
|
||
|
|
{
|
||
|
|
$response = $this->actingAs($this->admin)->getJson('/loom/themes/editor/read?theme=' . $this->themeName . '&path=index.blade.php');
|
||
|
|
$response->assertStatus(200);
|
||
|
|
$response->assertJson([
|
||
|
|
'content' => "<h1>Hello</h1>",
|
||
|
|
'extension' => 'php'
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_admin_can_save_file_content_and_creates_bak()
|
||
|
|
{
|
||
|
|
$newContent = "<h1>Updated</h1>";
|
||
|
|
$response = $this->actingAs($this->admin)->postJson('/loom/themes/editor/save', [
|
||
|
|
'theme' => $this->themeName,
|
||
|
|
'path' => 'index.blade.php',
|
||
|
|
'content' => $newContent
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertStatus(200);
|
||
|
|
$response->assertJson(['success' => true]);
|
||
|
|
|
||
|
|
$this->assertEquals($newContent, File::get($this->themePath . '/index.blade.php'));
|
||
|
|
$this->assertTrue(File::exists($this->themePath . '/index.blade.php.bak'));
|
||
|
|
$this->assertEquals("<h1>Hello</h1>", File::get($this->themePath . '/index.blade.php.bak'));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_admin_can_create_new_file()
|
||
|
|
{
|
||
|
|
$response = $this->actingAs($this->admin)->postJson('/loom/themes/editor/create', [
|
||
|
|
'theme' => $this->themeName,
|
||
|
|
'path' => 'assets/css',
|
||
|
|
'filename' => 'new.css'
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertStatus(200);
|
||
|
|
$this->assertTrue(File::exists($this->themePath . '/assets/css/new.css'));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_directory_traversal_protection()
|
||
|
|
{
|
||
|
|
// Try to read a file outside themes
|
||
|
|
$response = $this->actingAs($this->admin)->getJson('/loom/themes/editor/read?theme=' . $this->themeName . '&path=../../.env');
|
||
|
|
$response->assertStatus(403);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_invalid_extension_protection()
|
||
|
|
{
|
||
|
|
$response = $this->actingAs($this->admin)->postJson('/loom/themes/editor/create', [
|
||
|
|
'theme' => $this->themeName,
|
||
|
|
'path' => '',
|
||
|
|
'filename' => 'evil.exe'
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertStatus(422);
|
||
|
|
}
|
||
|
|
}
|