'array']); } private function makePollPost(array $attributes = []): Post { $postId = DB::table('posts')->insertGetId([ 'postable_id' => 1, 'postable_type' => Admin::class, 'created_at' => now(), 'updated_at' => now(), ]); foreach (['en' => 'mailing-poll-' . $postId, 'nl' => 'mailing-peiling-' . $postId] as $locale => $slug) { DB::table('post_translations')->insert([ 'post_id' => $postId, 'locale' => $locale, 'slug' => $slug, 'title' => "Poll title {$locale}", 'excerpt' => "Poll excerpt {$locale}", 'status' => 1, 'from' => now()->subDay(), 'created_at' => now(), 'updated_at' => now(), ]); } Poll::create(array_merge(['post_id' => $postId, 'status' => Poll::STATUS_OPEN], $attributes)); return Post::with(['translations', 'poll'])->find($postId); } public function test_poll_posts_get_the_poll_block(): void { $post = $this->makePollPost(); // No category needed: the poll relation decides $this->assertSame('poll', Mailing::determinePostType($post)); $this->assertSame('emails.newsletter.blocks.poll', Mailing::blockTemplate('poll')); // Without the config key (before config:merge) the default view is used config(['timebank_cc.mailing.templates.poll_block' => null, 'timebank-default.mailing.templates.poll_block' => null]); $this->assertSame('emails.newsletter.blocks.poll', Mailing::blockTemplate('poll')); } public function test_block_data_links_to_the_poll_page_and_shows_the_state(): void { $open = $this->makePollPost(['closes_at' => now()->addWeek()]); $data = Mailing::pollBlockData($open, 'nl'); $this->assertStringContainsString('/nl/peiling/mailing-peiling-' . $open->id, $data['url']); $this->assertFalse($data['poll_closed']); $this->assertNull($data['poll_opens']); $this->assertNotNull($data['poll_closes']); $later = $this->makePollPost(['opens_at' => now()->addDay()]); $this->assertNotNull(Mailing::pollBlockData($later, 'en')['poll_opens']); $closed = $this->makePollPost(['status' => Poll::STATUS_CLOSED]); $this->assertTrue(Mailing::pollBlockData($closed, 'en')['poll_closed']); // Not a poll: nothing added $plain = Post::withoutSyncingToSearch(fn () => Post::create(['postable_id' => 1, 'postable_type' => Admin::class])); $this->assertSame([], Mailing::pollBlockData($plain->load('translations'), 'en')); } public function test_polls_for_part_of_the_members_are_recognised(): void { $everyone = $this->makePollPost()->poll; $this->assertFalse($everyone->hasLimitedAudience()); // All four profile types ticked is still everyone $everyone->update(['target_profile_types' => Poll::PROFILE_TYPES]); $this->assertFalse($everyone->fresh()->hasLimitedAudience()); foreach ([ ['target_profile_types' => [\App\Models\Organization::class]], ['filter_by_location' => true], ['last_login_from' => '2026-01-01'], ['registered_until' => '2025-12-31'], ['member_scope' => Poll::MEMBER_SCOPE_MANAGERS_COORDINATORS], ] as $targeting) { $this->assertTrue($this->makePollPost($targeting)->poll->hasLimitedAudience(), json_encode($targeting)); } } public function test_test_mail_renders_the_poll_block_in_dutch(): void { $open = $this->makePollPost(['closes_at' => now()->addWeek()]); $closed = $this->makePollPost(['status' => Poll::STATUS_CLOSED]); $mailing = Mailing::create([ 'title' => 'Poll mailing', 'type' => 'local_newsletter', 'subject' => ['nl' => 'Onderwerp'], 'content_blocks' => [['post_id' => $open->id, 'order' => 1], ['post_id' => $closed->id, 'order' => 2]], ]); $html = (new TestNewsletterMail($mailing, 'nl'))->render(); $this->assertStringContainsString('Poll title nl', $html); $this->assertStringContainsString('/nl/peiling/mailing-peiling-' . $open->id, $html); $this->assertStringContainsString('Doe mee', $html); $this->assertStringContainsString('Open tot', $html); $this->assertStringContainsString('Bekijk de peiling', $html); $this->assertStringNotContainsString('/nl/post', $html); } }