Tag blog posts and sign them with a member
Tags are named in every language and made from the page form; the byline is the member who signs a post on the site. The public API sends both, filters posts by tag and lists the tags in use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ use App\Models\ReplyTemplate;
|
||||
use App\Models\Repository;
|
||||
use App\Models\Service;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Tag;
|
||||
use App\Models\Talk;
|
||||
use App\Models\Technology;
|
||||
use App\Models\Testimonial;
|
||||
@@ -118,6 +119,7 @@ class AdminApiContractTest extends TestCase
|
||||
$application = Application::factory()->for($call)->create();
|
||||
$application->replies()->create(['user_id' => $this->caller->id, 'user_name' => $this->caller->name, 'subject' => 'Re', 'body' => 'Hola', 'channel' => 'mail']);
|
||||
ReplyTemplate::factory()->create();
|
||||
$page->tags()->attach(Tag::factory()->create());
|
||||
$redirect = Redirect::factory()->create();
|
||||
$missingUrl = MissingUrl::factory()->create();
|
||||
$repository = Repository::factory()->create();
|
||||
@@ -158,6 +160,7 @@ class AdminApiContractTest extends TestCase
|
||||
'application' => ["/v1/applications/{$application->id}", '/v1/applications/{application}'],
|
||||
'replies' => ["/v1/applications/{$application->id}/replies", '/v1/applications/{application}/replies'],
|
||||
'reply templates' => ['/v1/reply-templates', '/v1/reply-templates'],
|
||||
'tags' => ['/v1/tags', '/v1/tags'],
|
||||
'redirects' => ['/v1/redirects', '/v1/redirects'],
|
||||
'redirect' => ["/v1/redirects/{$redirect->id}", '/v1/redirects/{redirect}'],
|
||||
'missing-urls' => ['/v1/missing-urls', '/v1/missing-urls'],
|
||||
|
||||
120
tests/Feature/Api/BlogTagsTest.php
Normal file
120
tests/Feature/Api/BlogTagsTest.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Api;
|
||||
|
||||
use App\Filament\Resources\Pages\Pages\EditPage;
|
||||
use App\Models\Member;
|
||||
use App\Models\Page;
|
||||
use App\Models\Tag;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tags and bylines for the blog: written in the panel or the API, read by the
|
||||
* public site.
|
||||
*/
|
||||
class BlogTagsTest extends TestCase
|
||||
{
|
||||
use FakesPireon, RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->seed(RolePermissionSeeder::class);
|
||||
Queue::fake();
|
||||
}
|
||||
|
||||
protected function tag(string $es, string $en): Tag
|
||||
{
|
||||
return Tag::factory()->create([
|
||||
'name' => ['es' => ucfirst($es), 'en' => ucfirst($en)],
|
||||
'slug' => ['es' => $es, 'en' => $en],
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_post_carries_its_tags_and_who_signs_it(): void
|
||||
{
|
||||
$ada = Member::factory()->create(['name' => 'Ada', 'slug' => 'ada', 'is_public' => true]);
|
||||
$page = Page::factory()->published()->create([
|
||||
'slug' => ['es' => 'angular-nativo'],
|
||||
'byline_member_id' => $ada->id,
|
||||
]);
|
||||
$page->tags()->attach([$this->tag('angular', 'angular')->id, $this->tag('rendimiento', 'performance')->id]);
|
||||
|
||||
$this->getJson('/api/public/v1/pages/angular-nativo?lang=es')
|
||||
->assertOk()
|
||||
->assertJsonPath('data.tags.1.slug', 'rendimiento')
|
||||
->assertJsonPath('data.byline.name', 'Ada')
|
||||
->assertJsonPath('data.byline.path', '/equipo/ada');
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_private_member_does_not_sign_in_public(): void
|
||||
{
|
||||
$page = Page::factory()->published()->create([
|
||||
'slug' => ['es' => 'secreto'],
|
||||
'byline_member_id' => Member::factory()->create(['is_public' => false])->id,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/public/v1/pages/secreto?lang=es')->assertJsonPath('data.byline', null);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function the_blog_is_read_by_tag(): void
|
||||
{
|
||||
$angular = $this->tag('angular', 'angular');
|
||||
Page::factory()->published()->create()->tags()->attach($angular);
|
||||
Page::factory()->published()->create()->tags()->attach($angular);
|
||||
Page::factory()->draft()->create()->tags()->attach($angular);
|
||||
Page::factory()->published()->create();
|
||||
|
||||
$this->getJson('/api/public/v1/pages?lang=es&tag=angular')->assertJsonCount(2, 'data');
|
||||
|
||||
$this->getJson('/api/public/v1/tags?lang=es')
|
||||
->assertOk()
|
||||
->assertJsonPath('data.0.slug', 'angular')
|
||||
->assertJsonPath('data.0.count', 2);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function the_admin_api_writes_tags_and_bylines(): void
|
||||
{
|
||||
$this->withHeaders($this->apiHeadersFor($this->userWithRoles('editor')));
|
||||
|
||||
$tag = $this->postJson('/api/v1/tags', ['name' => ['es' => 'Accesibilidad'], 'slug' => ['es' => 'accesibilidad']])
|
||||
->assertCreated()
|
||||
->json('data.id');
|
||||
|
||||
$member = Member::factory()->create();
|
||||
$page = Page::factory()->create();
|
||||
|
||||
$this->patchJson("/api/v1/pages/{$page->id}", ['tag_ids' => [$tag], 'byline_member_id' => $member->id])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.tag_ids', [$tag])
|
||||
->assertJsonPath('data.byline_member_id', $member->id);
|
||||
|
||||
// Untouched when not sent.
|
||||
$this->patchJson("/api/v1/pages/{$page->id}", ['is_featured' => true])->assertJsonPath('data.tag_ids', [$tag]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_tag_is_made_from_the_page_form(): void
|
||||
{
|
||||
$this->actingAs($this->userWithRoles('editor'));
|
||||
|
||||
$page = Page::factory()->draft()->create();
|
||||
|
||||
Livewire::test(EditPage::class, ['record' => $page->getRouteKey()])
|
||||
->assertFormFieldExists('tags')
|
||||
->assertFormFieldExists('byline_member_id');
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use App\Models\Member;
|
||||
use App\Models\Page;
|
||||
use App\Models\Project;
|
||||
use App\Models\Service;
|
||||
use App\Models\Tag;
|
||||
use App\Models\Talk;
|
||||
use App\Models\Technology;
|
||||
use App\Models\Testimonial;
|
||||
@@ -59,10 +60,12 @@ class PublicApiContractTest extends TestCase
|
||||
'slug' => ['es' => 'mi-proyecto'],
|
||||
]);
|
||||
|
||||
Page::factory()->published()->create([
|
||||
$page = Page::factory()->published()->create([
|
||||
'title' => ['es' => 'Quiénes somos'],
|
||||
'slug' => ['es' => 'quienes-somos'],
|
||||
'byline_member_id' => Member::factory()->create(['is_public' => true])->id,
|
||||
]);
|
||||
$page->tags()->attach(Tag::factory()->create());
|
||||
|
||||
Member::factory()->create([
|
||||
'name' => 'Ada Lovelace',
|
||||
|
||||
Reference in New Issue
Block a user