Duplicate records as drafts, and preview them from the form

The copy keeps the content, stack and team, but is unpublished, out of any
review, and gets a title marked as a copy and a free slug. It replaces
Filament's replicate action, which left the copy without a slug and still
published. The same copy is available through the API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 10:14:51 +02:00
parent 70521d4bc9
commit 366568b793
18 changed files with 486 additions and 64 deletions

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\Panel;
use App\Enums\PageStatus;
use App\Filament\Resources\Pages\Pages\EditPage;
use App\Filament\Resources\Projects\Pages\ListProjects;
use App\Models\Member;
use App\Models\Page;
use App\Models\Project;
use App\Models\Technology;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Livewire\Livewire;
use PHPUnit\Framework\Attributes\Test;
use Tests\FakesPireon;
use Tests\TestCase;
/**
* Duplicating: a draft copy to start the next record from, from the panel or
* the API.
*/
class DuplicateTest extends TestCase
{
use FakesPireon, RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->seed(RolePermissionSeeder::class);
Queue::fake();
}
#[Test]
public function a_published_page_is_copied_as_a_draft_with_a_free_slug(): void
{
$this->actingAs($editor = $this->userWithRoles('editor'));
$page = Page::factory()->published()->create([
'title' => ['es' => 'Hola', 'en' => 'Hello'],
'slug' => ['es' => 'hola', 'en' => 'hello'],
]);
Page::factory()->draft()->create(['slug' => ['es' => 'hola-copia']]);
Livewire::test(EditPage::class, ['record' => $page->getRouteKey()])
->callAction('duplicate')
->assertHasNoActionErrors();
$copy = Page::query()->latest('id')->first();
$this->assertSame(PageStatus::Draft, $copy->status);
$this->assertNull($copy->published_at);
$this->assertSame('Hola (copia)', $copy->getTranslation('title', 'es'));
$this->assertSame('Hello (copy)', $copy->getTranslation('title', 'en'));
$this->assertSame('hola-copia-2', $copy->getTranslation('slug', 'es'));
$this->assertSame($editor->id, $copy->author_id);
$this->assertSame($page->body, $copy->body);
}
#[Test]
public function a_project_keeps_its_stack_and_team(): void
{
$this->actingAs($this->userWithRoles('editor'));
$project = Project::factory()->published()->create();
$project->technologies()->attach(Technology::factory()->create());
$project->members()->attach(Member::factory()->create(), ['role' => 'Responsable']);
Livewire::test(ListProjects::class)->callTableAction('duplicate', $project);
$copy = Project::query()->latest('id')->first();
$this->assertNotSame($project->id, $copy->id);
$this->assertNull($copy->published_at);
$this->assertCount(1, $copy->technologies);
$this->assertSame('Responsable', $copy->members()->first()->pivot->role);
}
#[Test]
public function the_api_duplicates_too_and_only_for_who_may_create(): void
{
$page = Page::factory()->create();
$this->withHeaders($this->apiHeadersFor($this->userWithRoles('viewer')));
$this->postJson("/api/v1/pages/{$page->id}/duplicate")->assertForbidden();
$this->withHeaders($this->apiHeadersFor($this->userWithRoles('editor')));
$this->postJson("/api/v1/pages/{$page->id}/duplicate")
->assertCreated()
->assertJsonPath('data.type', 'pages');
$this->assertSame(2, Page::query()->count());
}
}