- Fix probundle exports missing images (double slides/ prefix in storage path) - Replace manual ZipArchive with PresentationBundle + ProBundleWriter from parser plugin - Add per-agenda-item download route and buttons for songs and slide items - Remove text layer from image-only slides in .pro generation - Fix image conversion: upscale small images, black bars on 2 sides max (contain) - Add upload warnings for non-16:9 and sub-1920x1080 images (German, non-blocking) - Update SlideFactory and all tests to use slides/ prefix in stored_filename - Add 11 new tests (agenda download, image conversion, upload warnings)
153 lines
5.8 KiB
PHP
153 lines
5.8 KiB
PHP
<?php
|
|
|
|
use App\Services\FileConversionService;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
beforeEach(function () {
|
|
Storage::fake('public');
|
|
});
|
|
|
|
test('convert image creates 1920x1080 jpg with black bars and thumbnail', function () {
|
|
$service = app(FileConversionService::class);
|
|
$file = makePngUpload('landscape.png', 400, 300);
|
|
|
|
$result = $service->convertImage($file);
|
|
|
|
expect($result)->toHaveKeys(['filename', 'thumbnail']);
|
|
expect($result['filename'])->toStartWith('slides/')->toEndWith('.jpg');
|
|
expect($result['thumbnail'])->toStartWith('slides/thumbnails/')->toEndWith('.jpg');
|
|
|
|
expect(Storage::disk('public')->exists($result['filename']))->toBeTrue();
|
|
expect(Storage::disk('public')->exists($result['thumbnail']))->toBeTrue();
|
|
|
|
$mainPath = Storage::disk('public')->path($result['filename']);
|
|
[$width, $height] = getimagesize($mainPath);
|
|
|
|
expect($width)->toBe(1920);
|
|
expect($height)->toBe(1080);
|
|
|
|
$image = imagecreatefromjpeg($mainPath);
|
|
expect($image)->not->toBeFalse();
|
|
|
|
$corner = imagecolorsforindex($image, imagecolorat($image, 0, 0));
|
|
$center = imagecolorsforindex($image, imagecolorat($image, 960, 540));
|
|
|
|
expect($corner['red'] + $corner['green'] + $corner['blue'])->toBeLessThan(20);
|
|
expect($center['red'])->toBeGreaterThan(100);
|
|
|
|
$thumbPath = Storage::disk('public')->path($result['thumbnail']);
|
|
[$thumbWidth, $thumbHeight] = getimagesize($thumbPath);
|
|
|
|
expect($thumbWidth)->toBe(320);
|
|
expect($thumbHeight)->toBe(180);
|
|
});
|
|
|
|
test('small image is upscaled with at most two black bars', function () {
|
|
$service = app(FileConversionService::class);
|
|
// 4:3 image → taller than 16:9 → pillarbox (left/right only)
|
|
$file = makePngUpload('small.png', 400, 300);
|
|
|
|
$result = $service->convertImage($file);
|
|
$mainPath = Storage::disk('public')->path($result['filename']);
|
|
|
|
$image = imagecreatefromjpeg($mainPath);
|
|
expect($image)->not->toBeFalse();
|
|
|
|
// Left/right edges must be black (pillarbox)
|
|
$left = imagecolorsforindex($image, imagecolorat($image, 0, 540));
|
|
$right = imagecolorsforindex($image, imagecolorat($image, 1919, 540));
|
|
expect($left['red'] + $left['green'] + $left['blue'])->toBeLessThan(20);
|
|
expect($right['red'] + $right['green'] + $right['blue'])->toBeLessThan(20);
|
|
|
|
// Top/bottom center must be image content (red), NOT black
|
|
$topCenter = imagecolorsforindex($image, imagecolorat($image, 960, 0));
|
|
$bottomCenter = imagecolorsforindex($image, imagecolorat($image, 960, 1079));
|
|
expect($topCenter['red'])->toBeGreaterThan(100);
|
|
expect($bottomCenter['red'])->toBeGreaterThan(100);
|
|
});
|
|
|
|
test('exact 16:9 image has no black bars', function () {
|
|
$service = app(FileConversionService::class);
|
|
$file = makePngUpload('exact.png', 1920, 1080);
|
|
|
|
$result = $service->convertImage($file);
|
|
$mainPath = Storage::disk('public')->path($result['filename']);
|
|
|
|
$image = imagecreatefromjpeg($mainPath);
|
|
expect($image)->not->toBeFalse();
|
|
|
|
// All corners must be image content (red), no black bars
|
|
$tl = imagecolorsforindex($image, imagecolorat($image, 0, 0));
|
|
$tr = imagecolorsforindex($image, imagecolorat($image, 1919, 0));
|
|
$bl = imagecolorsforindex($image, imagecolorat($image, 0, 1079));
|
|
$br = imagecolorsforindex($image, imagecolorat($image, 1919, 1079));
|
|
expect($tl['red'])->toBeGreaterThan(100);
|
|
expect($tr['red'])->toBeGreaterThan(100);
|
|
expect($bl['red'])->toBeGreaterThan(100);
|
|
expect($br['red'])->toBeGreaterThan(100);
|
|
});
|
|
|
|
test('small 16:9 image is upscaled without black bars', function () {
|
|
$service = app(FileConversionService::class);
|
|
$file = makePngUpload('small169.png', 320, 180);
|
|
|
|
$result = $service->convertImage($file);
|
|
$mainPath = Storage::disk('public')->path($result['filename']);
|
|
|
|
[$width, $height] = getimagesize($mainPath);
|
|
expect($width)->toBe(1920);
|
|
expect($height)->toBe(1080);
|
|
|
|
$image = imagecreatefromjpeg($mainPath);
|
|
expect($image)->not->toBeFalse();
|
|
|
|
// All corners must be image content (red), no black bars
|
|
$tl = imagecolorsforindex($image, imagecolorat($image, 0, 0));
|
|
$br = imagecolorsforindex($image, imagecolorat($image, 1919, 1079));
|
|
expect($tl['red'])->toBeGreaterThan(100);
|
|
expect($br['red'])->toBeGreaterThan(100);
|
|
});
|
|
|
|
test('portrait image gets pillarbox bars on left and right', function () {
|
|
$service = app(FileConversionService::class);
|
|
$file = makePngUpload('portrait.png', 1080, 1920);
|
|
|
|
$result = $service->convertImage($file);
|
|
$mainPath = Storage::disk('public')->path($result['filename']);
|
|
|
|
[$width, $height] = getimagesize($mainPath);
|
|
expect($width)->toBe(1920);
|
|
expect($height)->toBe(1080);
|
|
|
|
$image = imagecreatefromjpeg($mainPath);
|
|
expect($image)->not->toBeFalse();
|
|
|
|
$left = imagecolorsforindex($image, imagecolorat($image, 10, 540));
|
|
$right = imagecolorsforindex($image, imagecolorat($image, 1910, 540));
|
|
$center = imagecolorsforindex($image, imagecolorat($image, 960, 540));
|
|
|
|
expect($left['red'] + $left['green'] + $left['blue'])->toBeLessThan(20);
|
|
expect($right['red'] + $right['green'] + $right['blue'])->toBeLessThan(20);
|
|
expect($center['red'])->toBeGreaterThan(100);
|
|
});
|
|
|
|
function makePngUpload(string $name, int $width, int $height): UploadedFile
|
|
{
|
|
$path = tempnam(sys_get_temp_dir(), 'cts-img-');
|
|
if ($path === false) {
|
|
throw new RuntimeException('Temp-Datei konnte nicht erstellt werden.');
|
|
}
|
|
|
|
$image = imagecreatetruecolor($width, $height);
|
|
if ($image === false) {
|
|
throw new RuntimeException('Bild konnte nicht erstellt werden.');
|
|
}
|
|
|
|
$red = imagecolorallocate($image, 255, 0, 0);
|
|
imagefill($image, 0, 0, $red);
|
|
imagepng($image, $path);
|
|
|
|
return new UploadedFile($path, $name, 'image/png', null, true);
|
|
}
|