- populate COPYRIGHT (title/author/copyright/CCLI) + blank slides on every song; songHasContent ignores locked sections - foreground info/moderation images now bundle-relative (fixes blank images) - pre-added .probundle injection: Zip64-fix + verbatim .pro extraction (fixes empty bundle) - nametag subtitle split (text + subtitle); smaller non-bold render - skip songs with no content slides at export with German warning - link service agenda songs to SongDB edit modal via #song-<id> - allow CCLI import of metadata-only songs (no lyric sections) - expose has_content_slides on service songs; show "Keine Inhaltsfolien"
185 lines
5.7 KiB
PHP
185 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Service;
|
|
use App\Models\ServiceSong;
|
|
use App\Models\Song;
|
|
use App\Models\SongArrangement;
|
|
use App\Models\SongArrangementSection;
|
|
use App\Models\SongSection;
|
|
use App\Models\SongSlide;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
final class ServiceSongContentSlidesTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private function makeServiceWithSong(callable $buildSong): array
|
|
{
|
|
$service = Service::factory()->create([
|
|
'date' => Carbon::today(),
|
|
'finalized_at' => null,
|
|
]);
|
|
|
|
$song = Song::factory()->create();
|
|
$arrangement = $buildSong($song);
|
|
|
|
$serviceSong = ServiceSong::factory()->create([
|
|
'service_id' => $service->id,
|
|
'song_id' => $song->id,
|
|
'song_arrangement_id' => $arrangement->id,
|
|
'order' => 1,
|
|
]);
|
|
|
|
return [$service, $serviceSong];
|
|
}
|
|
|
|
private function defaultArrangementWithContentSlide(Song $song): SongArrangement
|
|
{
|
|
$arrangement = SongArrangement::factory()->create([
|
|
'song_id' => $song->id,
|
|
'is_default' => true,
|
|
]);
|
|
|
|
$section = SongSection::factory()->create([
|
|
'song_id' => $song->id,
|
|
'locked' => false,
|
|
]);
|
|
SongSlide::factory()->create(['song_section_id' => $section->id]);
|
|
|
|
SongArrangementSection::factory()->create([
|
|
'song_arrangement_id' => $arrangement->id,
|
|
'song_section_id' => $section->id,
|
|
'order' => 0,
|
|
]);
|
|
|
|
return $arrangement;
|
|
}
|
|
|
|
public function test_has_content_slides_ist_true_wenn_standard_arrangement_inhaltsfolie_hat(): void
|
|
{
|
|
Carbon::setTestNow('2026-03-01 10:00:00');
|
|
$this->withoutVite();
|
|
|
|
[$service, $serviceSong] = $this->makeServiceWithSong(
|
|
fn (Song $song) => $this->defaultArrangementWithContentSlide($song)
|
|
);
|
|
|
|
$response = $this->actingAs(User::factory()->create())
|
|
->get(route('services.edit', $service->id));
|
|
|
|
$response->assertInertia(
|
|
fn ($page) => $page
|
|
->component('Services/Edit')
|
|
->where('serviceSongs.0.has_content_slides', true)
|
|
);
|
|
}
|
|
|
|
public function test_has_content_slides_ist_false_wenn_section_gesperrt(): void
|
|
{
|
|
Carbon::setTestNow('2026-03-01 10:00:00');
|
|
$this->withoutVite();
|
|
|
|
[$service, $serviceSong] = $this->makeServiceWithSong(function (Song $song): SongArrangement {
|
|
$arrangement = SongArrangement::factory()->create([
|
|
'song_id' => $song->id,
|
|
'is_default' => true,
|
|
]);
|
|
|
|
// Locked section with slides -> not a content slide.
|
|
$section = SongSection::factory()->create([
|
|
'song_id' => $song->id,
|
|
'locked' => true,
|
|
]);
|
|
SongSlide::factory()->create(['song_section_id' => $section->id]);
|
|
|
|
SongArrangementSection::factory()->create([
|
|
'song_arrangement_id' => $arrangement->id,
|
|
'song_section_id' => $section->id,
|
|
'order' => 0,
|
|
]);
|
|
|
|
return $arrangement;
|
|
});
|
|
|
|
$response = $this->actingAs(User::factory()->create())
|
|
->get(route('services.edit', $service->id));
|
|
|
|
$response->assertInertia(
|
|
fn ($page) => $page
|
|
->component('Services/Edit')
|
|
->where('serviceSongs.0.has_content_slides', false)
|
|
);
|
|
}
|
|
|
|
public function test_has_content_slides_ist_false_wenn_section_keine_folien_hat(): void
|
|
{
|
|
Carbon::setTestNow('2026-03-01 10:00:00');
|
|
$this->withoutVite();
|
|
|
|
[$service, $serviceSong] = $this->makeServiceWithSong(function (Song $song): SongArrangement {
|
|
$arrangement = SongArrangement::factory()->create([
|
|
'song_id' => $song->id,
|
|
'is_default' => true,
|
|
]);
|
|
|
|
// Non-locked section but NO slides -> not a content slide.
|
|
$section = SongSection::factory()->create([
|
|
'song_id' => $song->id,
|
|
'locked' => false,
|
|
]);
|
|
|
|
SongArrangementSection::factory()->create([
|
|
'song_arrangement_id' => $arrangement->id,
|
|
'song_section_id' => $section->id,
|
|
'order' => 0,
|
|
]);
|
|
|
|
return $arrangement;
|
|
});
|
|
|
|
$response = $this->actingAs(User::factory()->create())
|
|
->get(route('services.edit', $service->id));
|
|
|
|
$response->assertInertia(
|
|
fn ($page) => $page
|
|
->component('Services/Edit')
|
|
->where('serviceSongs.0.has_content_slides', false)
|
|
);
|
|
}
|
|
|
|
public function test_has_content_slides_ist_null_wenn_kein_song_zugeordnet(): void
|
|
{
|
|
Carbon::setTestNow('2026-03-01 10:00:00');
|
|
$this->withoutVite();
|
|
|
|
$service = Service::factory()->create([
|
|
'date' => Carbon::today(),
|
|
'finalized_at' => null,
|
|
]);
|
|
|
|
ServiceSong::create([
|
|
'service_id' => $service->id,
|
|
'song_id' => null,
|
|
'song_arrangement_id' => null,
|
|
'use_translation' => false,
|
|
'order' => 1,
|
|
'cts_song_name' => 'Unmatched Song',
|
|
'cts_ccli_id' => '999999',
|
|
]);
|
|
|
|
$response = $this->actingAs(User::factory()->create())
|
|
->get(route('services.edit', $service->id));
|
|
|
|
$response->assertInertia(
|
|
fn ($page) => $page
|
|
->component('Services/Edit')
|
|
->where('serviceSongs.0.has_content_slides', null)
|
|
);
|
|
}
|
|
}
|