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', "

Hello

"); 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' => "

Hello

", 'extension' => 'php' ]); } public function test_admin_can_save_file_content_and_creates_bak() { $newContent = "

Updated

"; $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("

Hello

", 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); } }