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>
165 lines
5.4 KiB
PHP
165 lines
5.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Panel;
|
|
|
|
use App\Filament\Resources\Pages\PageResource;
|
|
use App\Filament\Resources\Pages\Pages\CreatePage;
|
|
use App\Filament\Resources\Pages\Pages\EditPage;
|
|
use App\Filament\Resources\Projects\Pages\CreateProject;
|
|
use App\Filament\Resources\Services\Pages\CreateService;
|
|
use App\Models\Page;
|
|
use App\Models\Project;
|
|
use App\Models\Service;
|
|
use App\Support\DuplicateTitles;
|
|
use Database\Seeders\RolePermissionSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\FakesPireon;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* A nudge, not a rule.
|
|
*
|
|
* The slug already has a hard uniqueness constraint — two records cannot
|
|
* answer at the same address. What this catches is the case that constraint
|
|
* cannot: two records with different addresses and almost the same title,
|
|
* which is what actually happens when nobody remembers something already
|
|
* exists. It warns; it has never once refused to save.
|
|
*/
|
|
class DuplicateTitlesTest extends TestCase
|
|
{
|
|
use FakesPireon, RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->seed(RolePermissionSeeder::class);
|
|
$this->actingAs($this->userWithRoles('admin'));
|
|
}
|
|
|
|
#[Test]
|
|
public function a_near_identical_title_is_found(): void
|
|
{
|
|
Page::factory()->create(['title' => ['es' => 'Gestor de socios']]);
|
|
|
|
$matches = DuplicateTitles::similarTo(Page::class, 'Gestor de socios v2', 'es', null);
|
|
|
|
$this->assertCount(1, $matches);
|
|
$this->assertSame('Gestor de socios', $matches->first()->getTranslation('title', 'es', false));
|
|
}
|
|
|
|
#[Test]
|
|
public function an_unrelated_title_is_not_a_match(): void
|
|
{
|
|
Page::factory()->create(['title' => ['es' => 'Gestor de socios']]);
|
|
|
|
$matches = DuplicateTitles::similarTo(Page::class, 'Convocatoria de primavera', 'es', null);
|
|
|
|
$this->assertTrue($matches->isEmpty());
|
|
}
|
|
|
|
/**
|
|
* The record being edited must never flag itself: typing in an existing
|
|
* page's own title field would otherwise "find" a perfect match on every
|
|
* keystroke.
|
|
*/
|
|
#[Test]
|
|
public function a_record_is_never_a_duplicate_of_itself(): void
|
|
{
|
|
$page = Page::factory()->create(['title' => ['es' => 'Gestor de socios']]);
|
|
|
|
$matches = DuplicateTitles::similarTo(Page::class, 'Gestor de socios', 'es', $page->id);
|
|
|
|
$this->assertTrue($matches->isEmpty());
|
|
}
|
|
|
|
#[Test]
|
|
public function an_empty_title_matches_nothing(): void
|
|
{
|
|
Page::factory()->create(['title' => ['es' => 'Gestor de socios']]);
|
|
|
|
$this->assertTrue(DuplicateTitles::similarTo(Page::class, '', 'es', null)->isEmpty());
|
|
$this->assertTrue(DuplicateTitles::similarTo(Page::class, ' ', 'es', null)->isEmpty());
|
|
}
|
|
|
|
#[Test]
|
|
public function only_the_closest_three_are_returned(): void
|
|
{
|
|
foreach (range(1, 5) as $n) {
|
|
Page::factory()->create(['title' => ['es' => "Gestor de socios {$n}"]]);
|
|
}
|
|
|
|
$matches = DuplicateTitles::similarTo(Page::class, 'Gestor de socios', 'es', null);
|
|
|
|
$this->assertCount(3, $matches);
|
|
}
|
|
|
|
#[Test]
|
|
public function typing_a_near_duplicate_title_warns_while_creating(): void
|
|
{
|
|
Page::factory()->create(['title' => ['es' => 'Quiénes somos']]);
|
|
|
|
Livewire::test(CreatePage::class)
|
|
->fillForm(['title' => ['es' => 'Quienes somos']])
|
|
->assertNotified(__('panel.duplicates.title'));
|
|
}
|
|
|
|
/**
|
|
* Editing an existing page must not warn about itself the moment the
|
|
* form is touched.
|
|
*/
|
|
#[Test]
|
|
public function editing_a_page_without_changing_its_title_does_not_warn(): void
|
|
{
|
|
$page = Page::factory()->create(['title' => ['es' => 'Quiénes somos']]);
|
|
|
|
Livewire::test(EditPage::class, ['record' => $page->getKey()])
|
|
->fillForm(['excerpt' => ['es' => 'Un resumen nuevo']])
|
|
->assertNotNotified(__('panel.duplicates.title'));
|
|
}
|
|
|
|
#[Test]
|
|
public function the_warning_links_to_the_existing_record(): void
|
|
{
|
|
$existing = Page::factory()->create(['title' => ['es' => 'Quiénes somos']]);
|
|
|
|
$component = Livewire::test(CreatePage::class)
|
|
->fillForm(['title' => ['es' => 'Quienes somos']]);
|
|
|
|
// Read ahead of `assertNotified()`, which pulls this same key from
|
|
// the session and would leave nothing behind for a second read.
|
|
$notified = collect(session('filament.claimed_notifications'))->first();
|
|
|
|
$component->assertNotified(__('panel.duplicates.title'));
|
|
|
|
$this->assertSame(
|
|
PageResource::getUrl('edit', ['record' => $existing]),
|
|
$notified['actions'][0]['url'] ?? null,
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_works_for_projects(): void
|
|
{
|
|
Project::factory()->create(['title' => ['es' => 'Web del colectivo']]);
|
|
|
|
Livewire::test(CreateProject::class)
|
|
->fillForm(['title' => ['es' => 'Web del colectivo v2']])
|
|
->assertNotified(__('panel.duplicates.title'));
|
|
}
|
|
|
|
#[Test]
|
|
public function it_works_for_services(): void
|
|
{
|
|
Service::factory()->create(['title' => ['es' => 'Auditoría técnica']]);
|
|
|
|
Livewire::test(CreateService::class)
|
|
->fillForm(['title' => ['es' => 'Auditoria tecnica']])
|
|
->assertNotified(__('panel.duplicates.title'));
|
|
}
|
|
}
|