mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 23:18:10 +00:00
test: use TrmnlPipeline::fake() to speed up test suite
This commit is contained in:
parent
42b515e322
commit
a1a57014b6
9 changed files with 119 additions and 102 deletions
|
|
@ -14,7 +14,7 @@
|
||||||
"ext-imagick": "*",
|
"ext-imagick": "*",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"bnussbau/laravel-trmnl-blade": "2.0.*",
|
"bnussbau/laravel-trmnl-blade": "2.0.*",
|
||||||
"bnussbau/trmnl-pipeline-php": "^0.2.0",
|
"bnussbau/trmnl-pipeline-php": "^0.3.0",
|
||||||
"keepsuit/laravel-liquid": "^0.5.2",
|
"keepsuit/laravel-liquid": "^0.5.2",
|
||||||
"laravel/framework": "^12.1",
|
"laravel/framework": "^12.1",
|
||||||
"laravel/sanctum": "^4.0",
|
"laravel/sanctum": "^4.0",
|
||||||
|
|
|
||||||
14
composer.lock
generated
14
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "f8f7d3fd0eba117ddeb5463047ac5493",
|
"content-hash": "7d12a2e6d66b2e82c6d96d6a0c5366f0",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "aws/aws-crt-php",
|
"name": "aws/aws-crt-php",
|
||||||
|
|
@ -243,16 +243,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "bnussbau/trmnl-pipeline-php",
|
"name": "bnussbau/trmnl-pipeline-php",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/bnussbau/trmnl-pipeline-php.git",
|
"url": "https://github.com/bnussbau/trmnl-pipeline-php.git",
|
||||||
"reference": "0a85e4c935a7009c469c014c6b7f2d9783d82523"
|
"reference": "89ceac9e0f35bdee591dfddd7b048aff1218bb6e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/bnussbau/trmnl-pipeline-php/zipball/0a85e4c935a7009c469c014c6b7f2d9783d82523",
|
"url": "https://api.github.com/repos/bnussbau/trmnl-pipeline-php/zipball/89ceac9e0f35bdee591dfddd7b048aff1218bb6e",
|
||||||
"reference": "0a85e4c935a7009c469c014c6b7f2d9783d82523",
|
"reference": "89ceac9e0f35bdee591dfddd7b048aff1218bb6e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -294,7 +294,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/bnussbau/trmnl-pipeline-php/issues",
|
"issues": "https://github.com/bnussbau/trmnl-pipeline-php/issues",
|
||||||
"source": "https://github.com/bnussbau/trmnl-pipeline-php/tree/0.2.0"
|
"source": "https://github.com/bnussbau/trmnl-pipeline-php/tree/0.3.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -310,7 +310,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-09-18T16:40:28+00:00"
|
"time": "2025-09-24T16:29:38+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
|
|
|
||||||
|
|
@ -383,7 +383,6 @@ new class extends Component {
|
||||||
<flux:heading size="lg">Edit TRMNL</flux:heading>
|
<flux:heading size="lg">Edit TRMNL</flux:heading>
|
||||||
<flux:subheading></flux:subheading>
|
<flux:subheading></flux:subheading>
|
||||||
</div>
|
</div>
|
||||||
<!-- @dump($device) -->
|
|
||||||
<flux:input label="Name" wire:model="name"/>
|
<flux:input label="Name" wire:model="name"/>
|
||||||
|
|
||||||
<flux:input label="API Key" icon="key" value="{{ $device->api_key }}" type="password"
|
<flux:input label="API Key" icon="key" value="{{ $device->api_key }}" type="password"
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use App\Models\Playlist;
|
||||||
use App\Models\PlaylistItem;
|
use App\Models\PlaylistItem;
|
||||||
use App\Models\Plugin;
|
use App\Models\Plugin;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
|
||||||
use Illuminate\Support\Facades\Queue;
|
use Illuminate\Support\Facades\Queue;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Laravel\Sanctum\Sanctum;
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
|
@ -14,6 +15,7 @@ use Laravel\Sanctum\Sanctum;
|
||||||
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
TrmnlPipeline::fake();
|
||||||
Storage::fake('public');
|
Storage::fake('public');
|
||||||
Storage::disk('public')->makeDirectory('/images/generated');
|
Storage::disk('public')->makeDirectory('/images/generated');
|
||||||
});
|
});
|
||||||
|
|
@ -573,7 +575,7 @@ test('plugin caches image until data is stale', function () {
|
||||||
|
|
||||||
expect($thirdResponse['filename'])
|
expect($thirdResponse['filename'])
|
||||||
->not->toBe($firstResponse['filename']);
|
->not->toBe($firstResponse['filename']);
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
test('plugins in playlist are rendered in order', function () {
|
test('plugins in playlist are rendered in order', function () {
|
||||||
// Create source device with a playlist
|
// Create source device with a playlist
|
||||||
|
|
@ -677,7 +679,7 @@ test('plugins in playlist are rendered in order', function () {
|
||||||
$thirdResponse->assertOk();
|
$thirdResponse->assertOk();
|
||||||
expect($thirdResponse['filename'])
|
expect($thirdResponse['filename'])
|
||||||
->not->toBe($secondResponse['filename']);
|
->not->toBe($secondResponse['filename']);
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
test('display endpoint updates last_refreshed_at timestamp', function () {
|
test('display endpoint updates last_refreshed_at timestamp', function () {
|
||||||
$device = Device::factory()->create([
|
$device = Device::factory()->create([
|
||||||
|
|
@ -787,7 +789,7 @@ test('display endpoint handles mashup playlist items correctly', function () {
|
||||||
// Verify the playlist item was marked as displayed
|
// Verify the playlist item was marked as displayed
|
||||||
$playlistItem->refresh();
|
$playlistItem->refresh();
|
||||||
expect($playlistItem->last_displayed_at)->not->toBeNull();
|
expect($playlistItem->last_displayed_at)->not->toBeNull();
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
test('device in sleep mode returns sleep image and correct refresh rate', function () {
|
test('device in sleep mode returns sleep image and correct refresh rate', function () {
|
||||||
$device = Device::factory()->create([
|
$device = Device::factory()->create([
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,15 @@ test('oidc test command has correct signature', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('oidc test command runs successfully with disabled oidc', function () {
|
test('oidc test command runs successfully with disabled oidc', function () {
|
||||||
config(['services.oidc.enabled' => false]);
|
config([
|
||||||
|
'app.url' => 'http://localhost',
|
||||||
|
'services.oidc.enabled' => false,
|
||||||
|
'services.oidc.endpoint' => null,
|
||||||
|
'services.oidc.client_id' => null,
|
||||||
|
'services.oidc.client_secret' => null,
|
||||||
|
'services.oidc.redirect' => null,
|
||||||
|
'services.oidc.scopes' => [],
|
||||||
|
]);
|
||||||
|
|
||||||
$this->artisan('oidc:test')
|
$this->artisan('oidc:test')
|
||||||
->expectsOutput('Testing OIDC Configuration...')
|
->expectsOutput('Testing OIDC Configuration...')
|
||||||
|
|
@ -34,6 +42,7 @@ test('oidc test command runs successfully with disabled oidc', function () {
|
||||||
|
|
||||||
test('oidc test command runs successfully with enabled oidc but missing config', function () {
|
test('oidc test command runs successfully with enabled oidc but missing config', function () {
|
||||||
config([
|
config([
|
||||||
|
'app.url' => 'http://localhost',
|
||||||
'services.oidc.enabled' => true,
|
'services.oidc.enabled' => true,
|
||||||
'services.oidc.endpoint' => null,
|
'services.oidc.endpoint' => null,
|
||||||
'services.oidc.client_id' => null,
|
'services.oidc.client_id' => null,
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
use App\Jobs\GenerateScreenJob;
|
use App\Jobs\GenerateScreenJob;
|
||||||
use App\Models\Device;
|
use App\Models\Device;
|
||||||
|
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
TrmnlPipeline::fake();
|
||||||
Storage::fake('public');
|
Storage::fake('public');
|
||||||
Storage::disk('public')->makeDirectory('/images/generated');
|
Storage::disk('public')->makeDirectory('/images/generated');
|
||||||
});
|
});
|
||||||
|
|
@ -23,7 +25,7 @@ test('it generates screen images and updates device', function () {
|
||||||
// Assert both PNG and BMP files were created
|
// Assert both PNG and BMP files were created
|
||||||
$uuid = $device->current_screen_image;
|
$uuid = $device->current_screen_image;
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
test('it cleans up unused images', function () {
|
test('it cleans up unused images', function () {
|
||||||
// Create some test devices with images
|
// Create some test devices with images
|
||||||
|
|
@ -45,7 +47,7 @@ test('it cleans up unused images', function () {
|
||||||
Storage::disk('public')->assertMissing('/images/generated/uuid-to-be-replaced.bmp');
|
Storage::disk('public')->assertMissing('/images/generated/uuid-to-be-replaced.bmp');
|
||||||
Storage::disk('public')->assertMissing('/images/generated/inactive-uuid.png');
|
Storage::disk('public')->assertMissing('/images/generated/inactive-uuid.png');
|
||||||
Storage::disk('public')->assertMissing('/images/generated/inactive-uuid.bmp');
|
Storage::disk('public')->assertMissing('/images/generated/inactive-uuid.bmp');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
test('it preserves gitignore file during cleanup', function () {
|
test('it preserves gitignore file during cleanup', function () {
|
||||||
Storage::disk('public')->put('/images/generated/.gitignore', '*');
|
Storage::disk('public')->put('/images/generated/.gitignore', '*');
|
||||||
|
|
@ -55,4 +57,4 @@ test('it preserves gitignore file during cleanup', function () {
|
||||||
$job->handle();
|
$job->handle();
|
||||||
|
|
||||||
Storage::disk('public')->assertExists('/images/generated/.gitignore');
|
Storage::disk('public')->assertExists('/images/generated/.gitignore');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use App\Enums\ImageFormat;
|
||||||
use App\Models\Device;
|
use App\Models\Device;
|
||||||
use App\Models\DeviceModel;
|
use App\Models\DeviceModel;
|
||||||
use App\Services\ImageGenerationService;
|
use App\Services\ImageGenerationService;
|
||||||
|
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
|
@ -14,6 +15,11 @@ uses(RefreshDatabase::class);
|
||||||
beforeEach(function (): void {
|
beforeEach(function (): void {
|
||||||
Storage::fake('public');
|
Storage::fake('public');
|
||||||
Storage::disk('public')->makeDirectory('/images/generated');
|
Storage::disk('public')->makeDirectory('/images/generated');
|
||||||
|
TrmnlPipeline::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function (): void {
|
||||||
|
TrmnlPipeline::restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates image for device without device model', function (): void {
|
it('generates image for device without device model', function (): void {
|
||||||
|
|
@ -34,7 +40,7 @@ it('generates image for device without device model', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created
|
// Assert PNG file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('generates image for device with device model', function (): void {
|
it('generates image for device with device model', function (): void {
|
||||||
// Create a DeviceModel
|
// Create a DeviceModel
|
||||||
|
|
@ -64,68 +70,7 @@ it('generates image for device with device model', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created
|
// Assert PNG file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('generates 4-color 2-bit PNG with device model', function (): void {
|
|
||||||
// Create a DeviceModel for 4-color, 2-bit PNG
|
|
||||||
$deviceModel = DeviceModel::factory()->create([
|
|
||||||
'width' => 800,
|
|
||||||
'height' => 480,
|
|
||||||
'colors' => 4,
|
|
||||||
'bit_depth' => 2,
|
|
||||||
'scale_factor' => 1.0,
|
|
||||||
'rotation' => 0,
|
|
||||||
'mime_type' => 'image/png',
|
|
||||||
'offset_x' => 0,
|
|
||||||
'offset_y' => 0,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create a device with the DeviceModel
|
|
||||||
$device = Device::factory()->create([
|
|
||||||
'device_model_id' => $deviceModel->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$markup = '<div style="background: white; color: black; padding: 20px;">Test Content</div>';
|
|
||||||
$uuid = ImageGenerationService::generateImage($markup, $device->id);
|
|
||||||
|
|
||||||
// Assert the device was updated with a new image UUID
|
|
||||||
$device->refresh();
|
|
||||||
expect($device->current_screen_image)->toBe($uuid);
|
|
||||||
|
|
||||||
// Assert PNG file was created
|
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
|
||||||
|
|
||||||
// Verify the image file has content and isn't blank
|
|
||||||
$imagePath = Storage::disk('public')->path("/images/generated/{$uuid}.png");
|
|
||||||
$imageSize = filesize($imagePath);
|
|
||||||
expect($imageSize)->toBeGreaterThan(200); // Should be at least 200 bytes for a 2-bit PNG
|
|
||||||
|
|
||||||
// Verify it's a valid PNG file
|
|
||||||
$imageInfo = getimagesize($imagePath);
|
|
||||||
expect($imageInfo[0])->toBe(800); // Width
|
|
||||||
expect($imageInfo[1])->toBe(480); // Height
|
|
||||||
expect($imageInfo[2])->toBe(IMAGETYPE_PNG); // PNG type
|
|
||||||
|
|
||||||
// Debug: Check if the image has any non-transparent pixels
|
|
||||||
$image = imagecreatefrompng($imagePath);
|
|
||||||
$width = imagesx($image);
|
|
||||||
$height = imagesy($image);
|
|
||||||
$hasContent = false;
|
|
||||||
|
|
||||||
// Check a few sample pixels to see if there's content
|
|
||||||
for ($x = 0; $x < min(10, $width); $x += 2) {
|
|
||||||
for ($y = 0; $y < min(10, $height); $y += 2) {
|
|
||||||
$color = imagecolorat($image, $x, $y);
|
|
||||||
if ($color !== 0) { // Not black/transparent
|
|
||||||
$hasContent = true;
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
imagedestroy($image);
|
|
||||||
expect($hasContent)->toBe(true, 'Image should contain visible content');
|
|
||||||
})->skipOnCi();
|
|
||||||
|
|
||||||
it('generates BMP with device model', function (): void {
|
it('generates BMP with device model', function (): void {
|
||||||
// Create a DeviceModel for BMP format
|
// Create a DeviceModel for BMP format
|
||||||
|
|
@ -155,7 +100,7 @@ it('generates BMP with device model', function (): void {
|
||||||
|
|
||||||
// Assert BMP file was created
|
// Assert BMP file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.bmp");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.bmp");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('applies scale factor from device model', function (): void {
|
it('applies scale factor from device model', function (): void {
|
||||||
// Create a DeviceModel with scale factor
|
// Create a DeviceModel with scale factor
|
||||||
|
|
@ -185,7 +130,7 @@ it('applies scale factor from device model', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created
|
// Assert PNG file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('applies rotation from device model', function (): void {
|
it('applies rotation from device model', function (): void {
|
||||||
// Create a DeviceModel with rotation
|
// Create a DeviceModel with rotation
|
||||||
|
|
@ -215,7 +160,7 @@ it('applies rotation from device model', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created
|
// Assert PNG file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('applies offset from device model', function (): void {
|
it('applies offset from device model', function (): void {
|
||||||
// Create a DeviceModel with offset
|
// Create a DeviceModel with offset
|
||||||
|
|
@ -245,7 +190,7 @@ it('applies offset from device model', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created
|
// Assert PNG file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('falls back to device settings when no device model', function (): void {
|
it('falls back to device settings when no device model', function (): void {
|
||||||
// Create a device with custom settings but no DeviceModel
|
// Create a device with custom settings but no DeviceModel
|
||||||
|
|
@ -265,7 +210,7 @@ it('falls back to device settings when no device model', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created
|
// Assert PNG file was created
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('handles auto image format for legacy devices', function (): void {
|
it('handles auto image format for legacy devices', function (): void {
|
||||||
// Create a device with AUTO format (legacy behavior)
|
// Create a device with AUTO format (legacy behavior)
|
||||||
|
|
@ -286,7 +231,7 @@ it('handles auto image format for legacy devices', function (): void {
|
||||||
|
|
||||||
// Assert PNG file was created (modern firmware defaults to PNG)
|
// Assert PNG file was created (modern firmware defaults to PNG)
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('cleanupFolder removes unused images', function (): void {
|
it('cleanupFolder removes unused images', function (): void {
|
||||||
// Create active devices with images
|
// Create active devices with images
|
||||||
|
|
@ -309,7 +254,7 @@ it('cleanupFolder removes unused images', function (): void {
|
||||||
// Assert inactive files are removed
|
// Assert inactive files are removed
|
||||||
Storage::disk('public')->assertMissing('/images/generated/inactive-uuid.png');
|
Storage::disk('public')->assertMissing('/images/generated/inactive-uuid.png');
|
||||||
Storage::disk('public')->assertMissing('/images/generated/another-inactive.png');
|
Storage::disk('public')->assertMissing('/images/generated/another-inactive.png');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('cleanupFolder preserves .gitignore', function (): void {
|
it('cleanupFolder preserves .gitignore', function (): void {
|
||||||
// Create gitignore file
|
// Create gitignore file
|
||||||
|
|
@ -323,7 +268,7 @@ it('cleanupFolder preserves .gitignore', function (): void {
|
||||||
|
|
||||||
// Assert gitignore is preserved
|
// Assert gitignore is preserved
|
||||||
Storage::disk('public')->assertExists('/images/generated/.gitignore');
|
Storage::disk('public')->assertExists('/images/generated/.gitignore');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('resetIfNotCacheable resets when device models exist', function (): void {
|
it('resetIfNotCacheable resets when device models exist', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -340,7 +285,7 @@ it('resetIfNotCacheable resets when device models exist', function (): void {
|
||||||
// Assert plugin image was reset
|
// Assert plugin image was reset
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBeNull();
|
expect($plugin->current_image)->toBeNull();
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('resetIfNotCacheable resets when custom dimensions exist', function (): void {
|
it('resetIfNotCacheable resets when custom dimensions exist', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -358,7 +303,7 @@ it('resetIfNotCacheable resets when custom dimensions exist', function (): void
|
||||||
// Assert plugin image was reset
|
// Assert plugin image was reset
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBeNull();
|
expect($plugin->current_image)->toBeNull();
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('resetIfNotCacheable preserves image for standard devices', function (): void {
|
it('resetIfNotCacheable preserves image for standard devices', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -377,7 +322,7 @@ it('resetIfNotCacheable preserves image for standard devices', function (): void
|
||||||
// Assert plugin image was preserved
|
// Assert plugin image was preserved
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBe('test-uuid');
|
expect($plugin->current_image)->toBe('test-uuid');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('determines correct image format from device model', function (): void {
|
it('determines correct image format from device model', function (): void {
|
||||||
// Test BMP format detection
|
// Test BMP format detection
|
||||||
|
|
@ -422,7 +367,7 @@ it('determines correct image format from device model', function (): void {
|
||||||
$device3->refresh();
|
$device3->refresh();
|
||||||
expect($device3->current_screen_image)->toBe($uuid3);
|
expect($device3->current_screen_image)->toBe($uuid3);
|
||||||
Storage::disk('public')->assertExists("/images/generated/{$uuid3}.png");
|
Storage::disk('public')->assertExists("/images/generated/{$uuid3}.png");
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('generates BMP for legacy device with bmp3_1bit_srgb format', function (): void {
|
it('generates BMP for legacy device with bmp3_1bit_srgb format', function (): void {
|
||||||
// Create a device with BMP format but no DeviceModel (legacy behavior)
|
// Create a device with BMP format but no DeviceModel (legacy behavior)
|
||||||
|
|
@ -454,4 +399,4 @@ it('generates BMP for legacy device with bmp3_1bit_srgb format', function (): vo
|
||||||
expect($imageInfo[0])->toBe(800); // Width
|
expect($imageInfo[0])->toBe(800); // Width
|
||||||
expect($imageInfo[1])->toBe(480); // Height
|
expect($imageInfo[1])->toBe(480); // Height
|
||||||
expect($imageInfo[2])->toBe(IMAGETYPE_BMP); // BMP type
|
expect($imageInfo[2])->toBe(IMAGETYPE_BMP); // BMP type
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
|
||||||
55
tests/Feature/ImageGenerationWithoutFakeTest.php
Normal file
55
tests/Feature/ImageGenerationWithoutFakeTest.php
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Device;
|
||||||
|
use App\Models\DeviceModel;
|
||||||
|
use App\Services\ImageGenerationService;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
Storage::fake('public');
|
||||||
|
Storage::disk('public')->makeDirectory('/images/generated');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates 4-color 2-bit PNG with device model', function (): void {
|
||||||
|
// Create a DeviceModel for 4-color, 2-bit PNG
|
||||||
|
$deviceModel = DeviceModel::factory()->create([
|
||||||
|
'width' => 800,
|
||||||
|
'height' => 480,
|
||||||
|
'colors' => 4,
|
||||||
|
'bit_depth' => 2,
|
||||||
|
'scale_factor' => 1.0,
|
||||||
|
'rotation' => 0,
|
||||||
|
'mime_type' => 'image/png',
|
||||||
|
'offset_x' => 0,
|
||||||
|
'offset_y' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a device with the DeviceModel
|
||||||
|
$device = Device::factory()->create([
|
||||||
|
'device_model_id' => $deviceModel->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$markup = '<div style="background: white; color: black; padding: 20px;">Test Content</div>';
|
||||||
|
$uuid = ImageGenerationService::generateImage($markup, $device->id);
|
||||||
|
|
||||||
|
// Assert the device was updated with a new image UUID
|
||||||
|
$device->refresh();
|
||||||
|
expect($device->current_screen_image)->toBe($uuid);
|
||||||
|
|
||||||
|
// Assert PNG file was created
|
||||||
|
Storage::disk('public')->assertExists("/images/generated/{$uuid}.png");
|
||||||
|
|
||||||
|
// Verify the image file has content and isn't blank
|
||||||
|
$imagePath = Storage::disk('public')->path("/images/generated/{$uuid}.png");
|
||||||
|
$imageSize = filesize($imagePath);
|
||||||
|
expect($imageSize)->toBeGreaterThan(200); // Should be at least 200 bytes for a 2-bit PNG
|
||||||
|
|
||||||
|
// Verify it's a valid PNG file
|
||||||
|
$imageInfo = getimagesize($imagePath);
|
||||||
|
expect($imageInfo[0])->toBe(800); // Width
|
||||||
|
expect($imageInfo[1])->toBe(480); // Height
|
||||||
|
expect($imageInfo[2])->toBe(IMAGETYPE_PNG); // PNG type
|
||||||
|
|
||||||
|
})->skipOnCI();
|
||||||
|
|
@ -6,10 +6,15 @@ use App\Enums\ImageFormat;
|
||||||
use App\Models\Device;
|
use App\Models\Device;
|
||||||
use App\Models\DeviceModel;
|
use App\Models\DeviceModel;
|
||||||
use App\Services\ImageGenerationService;
|
use App\Services\ImageGenerationService;
|
||||||
|
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
TrmnlPipeline::fake();
|
||||||
|
});
|
||||||
|
|
||||||
it('get_image_settings returns device model settings when available', function (): void {
|
it('get_image_settings returns device model settings when available', function (): void {
|
||||||
// Create a DeviceModel
|
// Create a DeviceModel
|
||||||
$deviceModel = DeviceModel::factory()->create([
|
$deviceModel = DeviceModel::factory()->create([
|
||||||
|
|
@ -47,7 +52,7 @@ it('get_image_settings returns device model settings when available', function (
|
||||||
expect($settings['offset_x'])->toBe(10);
|
expect($settings['offset_x'])->toBe(10);
|
||||||
expect($settings['offset_y'])->toBe(20);
|
expect($settings['offset_y'])->toBe(20);
|
||||||
expect($settings['use_model_settings'])->toBe(true);
|
expect($settings['use_model_settings'])->toBe(true);
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('get_image_settings falls back to device settings when no device model', function (): void {
|
it('get_image_settings falls back to device settings when no device model', function (): void {
|
||||||
// Create a device without DeviceModel
|
// Create a device without DeviceModel
|
||||||
|
|
@ -71,7 +76,7 @@ it('get_image_settings falls back to device settings when no device model', func
|
||||||
expect($settings['rotation'])->toBe(180);
|
expect($settings['rotation'])->toBe(180);
|
||||||
expect($settings['image_format'])->toBe(ImageFormat::PNG_8BIT_GRAYSCALE->value);
|
expect($settings['image_format'])->toBe(ImageFormat::PNG_8BIT_GRAYSCALE->value);
|
||||||
expect($settings['use_model_settings'])->toBe(false);
|
expect($settings['use_model_settings'])->toBe(false);
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('get_image_settings uses defaults for missing device properties', function (): void {
|
it('get_image_settings uses defaults for missing device properties', function (): void {
|
||||||
// Create a device without DeviceModel and missing properties
|
// Create a device without DeviceModel and missing properties
|
||||||
|
|
@ -101,7 +106,7 @@ it('get_image_settings uses defaults for missing device properties', function ()
|
||||||
expect($settings['offset_y'])->toBe(0);
|
expect($settings['offset_y'])->toBe(0);
|
||||||
// image_format defaults to 'auto' when not set
|
// image_format defaults to 'auto' when not set
|
||||||
expect($settings['image_format'])->toBe('auto');
|
expect($settings['image_format'])->toBe('auto');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('determine_image_format_from_model returns correct formats', function (): void {
|
it('determine_image_format_from_model returns correct formats', function (): void {
|
||||||
// Use reflection to access private method
|
// Use reflection to access private method
|
||||||
|
|
@ -153,7 +158,7 @@ it('determine_image_format_from_model returns correct formats', function (): voi
|
||||||
]);
|
]);
|
||||||
$format = $method->invoke(null, $unknownModel);
|
$format = $method->invoke(null, $unknownModel);
|
||||||
expect($format)->toBe(ImageFormat::AUTO->value);
|
expect($format)->toBe(ImageFormat::AUTO->value);
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('cleanup_folder identifies active images correctly', function (): void {
|
it('cleanup_folder identifies active images correctly', function (): void {
|
||||||
// Create devices with images
|
// Create devices with images
|
||||||
|
|
@ -189,7 +194,7 @@ it('reset_if_not_cacheable detects device models', function (): void {
|
||||||
|
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBeNull();
|
expect($plugin->current_image)->toBeNull();
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('reset_if_not_cacheable detects custom dimensions', function (): void {
|
it('reset_if_not_cacheable detects custom dimensions', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -206,7 +211,7 @@ it('reset_if_not_cacheable detects custom dimensions', function (): void {
|
||||||
|
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBeNull();
|
expect($plugin->current_image)->toBeNull();
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('reset_if_not_cacheable preserves cache for standard devices', function (): void {
|
it('reset_if_not_cacheable preserves cache for standard devices', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -224,7 +229,7 @@ it('reset_if_not_cacheable preserves cache for standard devices', function (): v
|
||||||
|
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBe('test-uuid');
|
expect($plugin->current_image)->toBe('test-uuid');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('reset_if_not_cacheable preserves cache for og_png and og_plus device models', function (): void {
|
it('reset_if_not_cacheable preserves cache for og_png and og_plus device models', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -255,7 +260,7 @@ it('reset_if_not_cacheable preserves cache for og_png and og_plus device models'
|
||||||
|
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBe('test-uuid');
|
expect($plugin->current_image)->toBe('test-uuid');
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('reset_if_not_cacheable resets cache for non-standard device models', function (): void {
|
it('reset_if_not_cacheable resets cache for non-standard device models', function (): void {
|
||||||
// Create a plugin
|
// Create a plugin
|
||||||
|
|
@ -277,12 +282,12 @@ it('reset_if_not_cacheable resets cache for non-standard device models', functio
|
||||||
|
|
||||||
$plugin->refresh();
|
$plugin->refresh();
|
||||||
expect($plugin->current_image)->toBeNull();
|
expect($plugin->current_image)->toBeNull();
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('reset_if_not_cacheable handles null plugin', function (): void {
|
it('reset_if_not_cacheable handles null plugin', function (): void {
|
||||||
// Test that the method handles null plugin gracefully
|
// Test that the method handles null plugin gracefully
|
||||||
expect(fn () => ImageGenerationService::resetIfNotCacheable(null))->not->toThrow(Exception::class);
|
expect(fn () => ImageGenerationService::resetIfNotCacheable(null))->not->toThrow(Exception::class);
|
||||||
})->skipOnCi();
|
});
|
||||||
|
|
||||||
it('image_format enum includes new 2bit 4c format', function (): void {
|
it('image_format enum includes new 2bit 4c format', function (): void {
|
||||||
// Test that the new format is properly defined in the enum
|
// Test that the new format is properly defined in the enum
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue