withHeaders(['Accept-Language' => 'es']); Queue::fake(); } #[Test] public function it_lists_published_services_in_the_order_they_were_arranged(): void { Service::factory()->create(['title' => ['es' => 'Segundo'], 'slug' => ['es' => 'segundo'], 'position' => 2]); Service::factory()->create(['title' => ['es' => 'Primero'], 'slug' => ['es' => 'primero'], 'position' => 1]); Service::factory()->draft()->create(['title' => ['es' => 'Borrador'], 'slug' => ['es' => 'borrador']]); $titles = $this->getJson('/api/public/v1/services') ->assertOk() ->json('data.*.title'); $this->assertSame(['Primero', 'Segundo'], $titles); } #[Test] public function a_service_resolves_by_slug_within_its_language(): void { Service::factory()->create([ 'title' => ['es' => 'Desarrollo', 'ca' => 'Desenvolupament'], 'slug' => ['es' => 'desarrollo', 'ca' => 'desenvolupament'], ]); $this->getJson('/api/public/v1/services/desarrollo') ->assertOk() ->assertJsonPath('data.title', 'Desarrollo'); $this->getJson('/api/public/v1/services/desenvolupament?lang=ca') ->assertOk() ->assertJsonPath('data.title', 'Desenvolupament'); // A Catalan slug must not answer in Spanish, exactly as everywhere // else in this API. $this->getJson('/api/public/v1/services/desenvolupament')->assertNotFound(); } #[Test] public function a_service_carries_the_work_that_backs_it(): void { $service = Service::factory()->create([ 'title' => ['es' => 'Desarrollo'], 'slug' => ['es' => 'desarrollo'], ]); $published = Project::factory()->published()->create([ 'title' => ['es' => 'Web de la asociación'], 'slug' => ['es' => 'web'], ]); $draft = Project::factory()->draft()->create([ 'title' => ['es' => 'A medias'], 'slug' => ['es' => 'a-medias'], ]); $service->projects()->attach([$published->id, $draft->id]); $data = $this->getJson('/api/public/v1/services/desarrollo')->assertOk()->json('data'); // Without real work behind it a service is a promise — but an // unpublished project is not something the public may see. $this->assertCount(1, $data['projects']); $this->assertSame('Web de la asociación', $data['projects'][0]['title']); } #[Test] public function a_draft_service_is_reachable_only_with_the_preview_secret(): void { config()->set('site.preview_token', 'secreto'); Service::factory()->draft()->create([ 'title' => ['es' => 'Sin publicar'], 'slug' => ['es' => 'sin-publicar'], ]); $this->getJson('/api/public/v1/services/sin-publicar')->assertNotFound(); $this->withHeaders(['X-Preview-Token' => 'secreto']) ->getJson('/api/public/v1/services/sin-publicar') ->assertOk(); } #[Test] public function the_wall_of_logos_leaves_out_whoever_asked_to_be_left_out(): void { $shy = Client::factory()->private()->create(['name' => 'Discreta']); $shown = Client::factory()->create(['name' => 'Visible']); Project::factory()->published()->create(['client_id' => $shy->id]); Project::factory()->published()->create(['client_id' => $shown->id]); $names = $this->getJson('/api/public/v1/clients')->assertOk()->json('data.*.name'); // Appearing on somebody else's website is their call, not ours. $this->assertSame(['Visible'], $names); } #[Test] public function a_client_with_nothing_published_is_not_a_logo_the_site_can_justify(): void { $withWork = Client::factory()->create(['name' => 'Con obra']); Project::factory()->published()->create(['client_id' => $withWork->id]); $withoutWork = Client::factory()->create(['name' => 'Sin obra']); Project::factory()->draft()->create(['client_id' => $withoutWork->id]); $names = $this->getJson('/api/public/v1/clients')->assertOk()->json('data.*.name'); $this->assertSame(['Con obra'], $names); // Unless the frontend asks for all of them anyway. $all = $this->getJson('/api/public/v1/clients?with_projects=0')->assertOk()->json('data.*.name'); $this->assertCount(2, $all); } #[Test] public function a_translated_quote_says_that_it_was_translated(): void { Testimonial::factory()->create([ 'quote' => ['es' => 'Nos cambiaron la forma de trabajar', 'en' => 'They changed how we work'], 'original_locale' => 'es', 'author_name' => 'Marta', ]); $this->getJson('/api/public/v1/testimonials') ->assertOk() ->assertJsonPath('data.0.is_original', true) ->assertJsonPath('data.0.original_locale', 'es'); // Read in English these are our words, not hers, and the frontend is // told so it can say "translated from Spanish" rather than pretend. $this->withHeaders(['Accept-Language' => 'en']) ->getJson('/api/public/v1/testimonials') ->assertOk() ->assertJsonPath('data.0.is_original', false) ->assertJsonPath('data.0.quote', 'They changed how we work'); } #[Test] public function testimonials_can_be_asked_for_by_what_they_vouch_for(): void { $client = Client::factory()->create(['name' => 'Asociación', 'slug' => 'asociacion']); $service = Service::factory()->create([ 'title' => ['es' => 'Formación'], 'slug' => ['es' => 'formacion'], ]); Testimonial::factory()->create(['client_id' => $client->id, 'author_name' => 'De cliente']); Testimonial::factory()->create(['service_id' => $service->id, 'author_name' => 'De servicio']); Testimonial::factory()->create(['author_name' => 'Suelto']); // Attached to something rather than to "the home page", so a page asks // for the ones that belong on it. $this->getJson('/api/public/v1/testimonials?client=asociacion') ->assertOk() ->assertJsonCount(1, 'data') ->assertJsonPath('data.0.author.name', 'De cliente'); $this->getJson('/api/public/v1/testimonials?service=formacion') ->assertOk() ->assertJsonCount(1, 'data') ->assertJsonPath('data.0.author.name', 'De servicio'); } #[Test] public function a_private_testimonial_never_leaves_the_panel(): void { Testimonial::factory()->private()->create(['author_name' => 'Sin permiso']); $this->getJson('/api/public/v1/testimonials') ->assertOk() ->assertJsonCount(0, 'data'); } #[Test] public function talks_come_back_newest_first_with_their_speaker(): void { $member = Member::factory()->create(['name' => 'Ada', 'slug' => 'ada']); Talk::factory()->create(['title' => 'La vieja', 'held_at' => now()->subYears(2), 'member_id' => $member->id]); Talk::factory()->create(['title' => 'La reciente', 'held_at' => now()->subMonth(), 'member_id' => $member->id]); Talk::factory()->create(['title' => 'Oculta', 'is_public' => false]); $data = $this->getJson('/api/public/v1/talks')->assertOk()->json('data'); $this->assertSame(['La reciente', 'La vieja'], array_column($data, 'title')); $this->assertSame('Ada', $data[0]['speaker']['name']); } #[Test] public function a_talks_title_is_not_translated(): void { Talk::factory()->create([ 'title' => 'Taming reactive Node.js', 'language' => 'en', 'summary' => ['es' => 'Sobre streams', 'en' => 'About streams'], ]); // The title is the name of a talk given once, in one language; // translating it would invent a talk that never happened. $this->getJson('/api/public/v1/talks') ->assertOk() ->assertJsonPath('data.0.title', 'Taming reactive Node.js') ->assertJsonPath('data.0.language', 'en') ->assertJsonPath('data.0.summary', 'Sobre streams'); } #[Test] public function a_project_now_carries_a_client_rather_than_a_typed_name(): void { $client = Client::factory()->create(['name' => 'Cruz Roja', 'slug' => 'cruz-roja']); Project::factory()->published()->create([ 'title' => ['es' => 'Su web'], 'slug' => ['es' => 'su-web'], 'client_id' => $client->id, ]); $data = $this->getJson('/api/public/v1/projects/su-web')->assertOk()->json('data'); $this->assertSame('Cruz Roja', $data['client']['name']); $this->assertSame('cruz-roja', $data['client']['slug']); // The old free-text pair is gone, not renamed. $this->assertArrayNotHasKey('beneficiary', $data); } #[Test] public function services_appear_in_the_url_list_the_frontend_builds_its_sitemap_from(): void { Service::factory()->create([ 'title' => ['es' => 'Desarrollo', 'ca' => 'Desenvolupament'], 'slug' => ['es' => 'desarrollo', 'ca' => 'desenvolupament'], ]); Service::factory()->draft()->create(['slug' => ['es' => 'borrador']]); $services = $this->getJson('/api/public/v1/slugs')->assertOk()->json('data.services'); $this->assertCount(1, $services); $this->assertSame('/servicios/desarrollo', $services[0]['alternates']['es']); $this->assertSame('/ca/serveis/desenvolupament', $services[0]['alternates']['ca']); } }