Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
167 lines
5.1 KiB
PHP
167 lines
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Panel;
|
|
|
|
use App\Filament\Pages\Trash;
|
|
use App\Models\Application;
|
|
use App\Models\Page;
|
|
use App\Models\Project;
|
|
use App\Models\User;
|
|
use App\Support\PreviewLink;
|
|
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;
|
|
|
|
/**
|
|
* The everyday tools: the bin and the shareable preview link.
|
|
*/
|
|
class PanelToolsTest extends TestCase
|
|
{
|
|
use FakesPireon, RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->seed(RolePermissionSeeder::class);
|
|
Queue::fake();
|
|
}
|
|
|
|
#[Test]
|
|
public function the_bin_gathers_everything_deleted(): void
|
|
{
|
|
Page::factory()->create(['title' => ['es' => 'Borrada']])->delete();
|
|
Project::factory()->create(['title' => ['es' => 'Borrado']])->delete();
|
|
|
|
$this->actingAs($this->userWithRoles('admin'));
|
|
|
|
// Recovering something used to mean remembering which resource it had
|
|
// been in and then turning on a filter.
|
|
$this->get(Trash::getUrl())
|
|
->assertSuccessful()
|
|
->assertSee('Borrada')
|
|
->assertSee('Borrado');
|
|
}
|
|
|
|
#[Test]
|
|
public function something_can_be_put_back_from_the_bin(): void
|
|
{
|
|
$page = Page::factory()->create();
|
|
$page->delete();
|
|
|
|
$this->actingAs($this->userWithRoles('admin'));
|
|
|
|
Livewire::test(Trash::class)
|
|
->call('restoreRecord', Page::class, $page->id);
|
|
|
|
$this->assertNull($page->fresh()->deleted_at);
|
|
}
|
|
|
|
#[Test]
|
|
public function the_bin_refuses_a_model_it_does_not_list(): void
|
|
{
|
|
$this->actingAs($this->userWithRoles('admin'));
|
|
|
|
// An id and a class name arriving from the browser must not be able to
|
|
// reach anything the page did not declare.
|
|
Livewire::test(Trash::class)
|
|
->call('restoreRecord', User::class, 1)
|
|
->assertOk();
|
|
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
#[Test]
|
|
public function a_viewer_sees_only_what_they_may_see(): void
|
|
{
|
|
Application::factory()->create()->delete();
|
|
Page::factory()->create(['title' => ['es' => 'Una página']])->delete();
|
|
|
|
$this->actingAs($this->userWithRoles('viewer'));
|
|
|
|
// A viewer has no business reading a stranger's message, in the bin or
|
|
// anywhere else.
|
|
$this->get(Trash::getUrl())
|
|
->assertSuccessful()
|
|
->assertSee('Una página')
|
|
->assertDontSee(__('panel.applications.plural'));
|
|
}
|
|
|
|
#[Test]
|
|
public function a_preview_link_points_at_the_record_it_was_made_for(): void
|
|
{
|
|
$project = Project::factory()->draft()->create([
|
|
'title' => ['es' => 'Borrador'],
|
|
'slug' => ['es' => 'borrador'],
|
|
]);
|
|
|
|
$url = PreviewLink::for($project, 'project', 'es');
|
|
|
|
$this->assertStringContainsString('/proyectos/borrador', $url);
|
|
$this->assertStringContainsString('preview=', $url);
|
|
|
|
$token = urldecode((string) parse_url($url, PHP_URL_QUERY));
|
|
$token = str_replace('preview=', '', $token);
|
|
|
|
$payload = PreviewLink::open($token);
|
|
|
|
$this->assertSame('project', $payload['type']);
|
|
$this->assertSame($project->id, $payload['id']);
|
|
$this->assertSame('es', $payload['locale']);
|
|
}
|
|
|
|
#[Test]
|
|
public function a_preview_link_opens_a_draft_on_the_api(): void
|
|
{
|
|
$project = Project::factory()->draft()->create([
|
|
'title' => ['es' => 'Borrador'],
|
|
'slug' => ['es' => 'borrador'],
|
|
]);
|
|
|
|
$url = PreviewLink::for($project, 'project', 'es');
|
|
$token = str_replace('preview=', '', urldecode((string) parse_url($url, PHP_URL_QUERY)));
|
|
|
|
$this->withHeaders(['Accept-Language' => 'es'])
|
|
->getJson('/api/public/v1/projects/borrador')
|
|
->assertNotFound();
|
|
|
|
$this->withHeaders(['Accept-Language' => 'es'])
|
|
->getJson('/api/public/v1/projects/borrador?preview='.urlencode($token))
|
|
->assertOk();
|
|
}
|
|
|
|
#[Test]
|
|
public function a_forged_or_expired_link_opens_nothing(): void
|
|
{
|
|
$this->assertNull(PreviewLink::open('no-es-un-token'));
|
|
|
|
$project = Project::factory()->draft()->create([
|
|
'title' => ['es' => 'Borrador'], 'slug' => ['es' => 'borrador'],
|
|
]);
|
|
|
|
$url = PreviewLink::for($project, 'project', 'es');
|
|
$token = str_replace('preview=', '', urldecode((string) parse_url($url, PHP_URL_QUERY)));
|
|
|
|
$this->travel(PreviewLink::HOURS + 1)->hours();
|
|
|
|
// Short-lived on purpose: one forwarded in an e-mail thread stops
|
|
// working rather than living for ever.
|
|
$this->assertNull(PreviewLink::open($token));
|
|
}
|
|
|
|
#[Test]
|
|
public function a_record_with_no_slug_in_a_language_has_no_link_for_it(): void
|
|
{
|
|
$project = Project::factory()->draft()->create(['slug' => ['es' => 'solo-es']]);
|
|
|
|
$this->assertNotNull(PreviewLink::for($project, 'project', 'es'));
|
|
$this->assertNull(PreviewLink::for($project, 'project', 'ca'));
|
|
}
|
|
}
|