diff --git a/app/Models/Plugin.php b/app/Models/Plugin.php
index c4b45c8..6f5d88b 100644
--- a/app/Models/Plugin.php
+++ b/app/Models/Plugin.php
@@ -11,7 +11,6 @@ use App\Liquid\Filters\StandardFilters;
use App\Liquid\Filters\StringMarkup;
use App\Liquid\Filters\Uniqueness;
use App\Liquid\Tags\TemplateTag;
-use App\Services\ImageGenerationService;
use App\Services\Plugin\Parsers\ResponseParserRegistry;
use App\Services\PluginImportService;
use Carbon\Carbon;
@@ -45,7 +44,6 @@ class Plugin extends Model
'no_bleed' => 'boolean',
'dark_mode' => 'boolean',
'preferred_renderer' => 'string',
- 'plugin_type' => 'string',
];
protected static function boot()
@@ -135,11 +133,6 @@ class Plugin extends Model
public function isDataStale(): bool
{
- // Image webhook plugins don't use data staleness - images are pushed directly
- if ($this->plugin_type === 'image_webhook') {
- return false;
- }
-
if ($this->data_strategy === 'webhook') {
// Treat as stale if any webhook event has occurred in the past hour
return $this->data_payload_updated_at && $this->data_payload_updated_at->gt(now()->subHour());
@@ -454,10 +447,6 @@ class Plugin extends Model
*/
public function render(string $size = 'full', bool $standalone = true, ?Device $device = null): string
{
- if ($this->plugin_type !== 'recipe') {
- throw new \InvalidArgumentException('Render method is only applicable for recipe plugins.');
- }
-
if ($this->render_markup) {
$renderedContent = '';
diff --git a/app/Services/ImageGenerationService.php b/app/Services/ImageGenerationService.php
index b8269a3..fcd5f12 100644
--- a/app/Services/ImageGenerationService.php
+++ b/app/Services/ImageGenerationService.php
@@ -280,10 +280,6 @@ class ImageGenerationService
public static function resetIfNotCacheable(?Plugin $plugin): void
{
if ($plugin?->id) {
- // Image webhook plugins have finalized images that shouldn't be reset
- if ($plugin->plugin_type === 'image_webhook') {
- return;
- }
// Check if any devices have custom dimensions or use non-standard DeviceModels
$hasCustomDimensions = Device::query()
->where(function ($query): void {
diff --git a/database/factories/PluginFactory.php b/database/factories/PluginFactory.php
index 10a1580..a2d2e65 100644
--- a/database/factories/PluginFactory.php
+++ b/database/factories/PluginFactory.php
@@ -29,24 +29,8 @@ class PluginFactory extends Factory
'icon_url' => null,
'flux_icon_name' => null,
'author_name' => $this->faker->name(),
- 'plugin_type' => 'recipe',
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];
}
-
- /**
- * Indicate that the plugin is an image webhook plugin.
- */
- public function imageWebhook(): static
- {
- return $this->state(fn (array $attributes): array => [
- 'plugin_type' => 'image_webhook',
- 'data_strategy' => 'static',
- 'data_stale_minutes' => 60,
- 'polling_url' => null,
- 'polling_verb' => 'get',
- 'name' => $this->faker->randomElement(['Camera Feed', 'Security Camera', 'Webcam', 'Image Stream']),
- ]);
- }
}
diff --git a/database/migrations/2026_01_05_153321_add_plugin_type_to_plugins_table.php b/database/migrations/2026_01_05_153321_add_plugin_type_to_plugins_table.php
deleted file mode 100644
index 558fe2c..0000000
--- a/database/migrations/2026_01_05_153321_add_plugin_type_to_plugins_table.php
+++ /dev/null
@@ -1,28 +0,0 @@
-string('plugin_type')->default('recipe')->after('uuid');
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::table('plugins', function (Blueprint $table): void {
- $table->dropColumn('plugin_type');
- });
- }
-};
diff --git a/resources/views/livewire/plugins/image-webhook-instance.blade.php b/resources/views/livewire/plugins/image-webhook-instance.blade.php
deleted file mode 100644
index e4ad9df..0000000
--- a/resources/views/livewire/plugins/image-webhook-instance.blade.php
+++ /dev/null
@@ -1,298 +0,0 @@
-user()->plugins->contains($this->plugin), 403);
- abort_unless($this->plugin->plugin_type === 'image_webhook', 404);
-
- $this->name = $this->plugin->name;
- }
-
- protected array $rules = [
- 'name' => 'required|string|max:255',
- 'checked_devices' => 'array',
- 'device_playlist_names' => 'array',
- 'device_playlists' => 'array',
- 'device_weekdays' => 'array',
- 'device_active_from' => 'array',
- 'device_active_until' => 'array',
- ];
-
- public function updateName(): void
- {
- abort_unless(auth()->user()->plugins->contains($this->plugin), 403);
- $this->validate(['name' => 'required|string|max:255']);
- $this->plugin->update(['name' => $this->name]);
- }
-
-
- public function addToPlaylist()
- {
- $this->validate([
- 'checked_devices' => 'required|array|min:1',
- ]);
-
- foreach ($this->checked_devices as $deviceId) {
- if (!isset($this->device_playlists[$deviceId]) || empty($this->device_playlists[$deviceId])) {
- $this->addError('device_playlists.' . $deviceId, 'Please select a playlist for each device.');
- return;
- }
-
- if ($this->device_playlists[$deviceId] === 'new') {
- if (!isset($this->device_playlist_names[$deviceId]) || empty($this->device_playlist_names[$deviceId])) {
- $this->addError('device_playlist_names.' . $deviceId, 'Playlist name is required when creating a new playlist.');
- return;
- }
- }
- }
-
- foreach ($this->checked_devices as $deviceId) {
- $playlist = null;
-
- if ($this->device_playlists[$deviceId] === 'new') {
- $playlist = \App\Models\Playlist::create([
- 'device_id' => $deviceId,
- 'name' => $this->device_playlist_names[$deviceId],
- 'weekdays' => !empty($this->device_weekdays[$deviceId] ?? null) ? $this->device_weekdays[$deviceId] : null,
- 'active_from' => $this->device_active_from[$deviceId] ?? null,
- 'active_until' => $this->device_active_until[$deviceId] ?? null,
- ]);
- } else {
- $playlist = \App\Models\Playlist::findOrFail($this->device_playlists[$deviceId]);
- }
-
- $maxOrder = $playlist->items()->max('order') ?? 0;
-
- // Image webhook plugins only support full layout
- $playlist->items()->create([
- 'plugin_id' => $this->plugin->id,
- 'order' => $maxOrder + 1,
- ]);
- }
-
- $this->reset([
- 'checked_devices',
- 'device_playlists',
- 'device_playlist_names',
- 'device_weekdays',
- 'device_active_from',
- 'device_active_until',
- ]);
- Flux::modal('add-to-playlist')->close();
- }
-
- public function getDevicePlaylists($deviceId)
- {
- return \App\Models\Playlist::where('device_id', $deviceId)->get();
- }
-
- public function hasAnyPlaylistSelected(): bool
- {
- foreach ($this->checked_devices as $deviceId) {
- if (isset($this->device_playlists[$deviceId]) && !empty($this->device_playlists[$deviceId])) {
- return true;
- }
- }
- return false;
- }
-
- public function deletePlugin(): void
- {
- abort_unless(auth()->user()->plugins->contains($this->plugin), 403);
- $this->plugin->delete();
- $this->redirect(route('plugins.image-webhook'));
- }
-
- public function getImagePath(): ?string
- {
- if (!$this->plugin->current_image) {
- return null;
- }
-
- $extensions = ['png', 'bmp'];
- foreach ($extensions as $ext) {
- $path = 'images/generated/'.$this->plugin->current_image.'.'.$ext;
- if (\Illuminate\Support\Facades\Storage::disk('public')->exists($path)) {
- return $path;
- }
- }
-
- return null;
- }
-};
-?>
-
-
-
-
-
Image Webhook – {{$plugin->name}}
-
-
-
- Add to Playlist
-
-
-
-
-
-
- Delete Instance
-
-
-
-
-
-
-
-
-
- Add to Playlist
-
-
-
-
-
-
-
-
-
Delete {{ $plugin->name }}?
-
This will also remove this instance from your playlists.
-
-
-
-
-
- Cancel
-
- Delete instance
-
-
-
-
-
-
-
-
- Webhook URL
-
- POST an image (PNG or BMP) to this URL to update the displayed image.
-
-
- Images must be posted in a format that can directly be read by the device. You need to take care of image format, dithering, and bit-depth. Check device logs if the image is not shown.
-
-
-
-
-
-
-
-
Current Image
- @if($this->getImagePath())
-
) }})
- @else
-
- No image uploaded yet. POST an image to the webhook URL to get started.
-
- @endif
-
-
-
-
-
-
diff --git a/resources/views/livewire/plugins/image-webhook.blade.php b/resources/views/livewire/plugins/image-webhook.blade.php
deleted file mode 100644
index 3161443..0000000
--- a/resources/views/livewire/plugins/image-webhook.blade.php
+++ /dev/null
@@ -1,163 +0,0 @@
- 'required|string|max:255',
- ];
-
- public function mount(): void
- {
- $this->refreshInstances();
- }
-
- public function refreshInstances(): void
- {
- $this->instances = auth()->user()
- ->plugins()
- ->where('plugin_type', 'image_webhook')
- ->orderBy('created_at', 'desc')
- ->get()
- ->toArray();
- }
-
- public function createInstance(): void
- {
- abort_unless(auth()->user() !== null, 403);
- $this->validate();
-
- Plugin::create([
- 'uuid' => Str::uuid(),
- 'user_id' => auth()->id(),
- 'name' => $this->name,
- 'plugin_type' => 'image_webhook',
- 'data_strategy' => 'static', // Not used for image_webhook, but required
- 'data_stale_minutes' => 60, // Not used for image_webhook, but required
- ]);
-
- $this->reset(['name']);
- $this->refreshInstances();
-
- Flux::modal('create-instance')->close();
- }
-
- public function deleteInstance(int $pluginId): void
- {
- abort_unless(auth()->user() !== null, 403);
-
- $plugin = Plugin::where('id', $pluginId)
- ->where('user_id', auth()->id())
- ->where('plugin_type', 'image_webhook')
- ->firstOrFail();
-
- $plugin->delete();
- $this->refreshInstances();
- }
-};
-?>
-
-
-
-
-
Image Webhook
- Plugin
-
-
- Create Instance
-
-
-
-
-
-
- Create Image Webhook Instance
- Create a new instance that accepts images via webhook
-
-
-
-
-
-
- @if(empty($instances))
-
-
- No instances yet
- Create your first Image Webhook instance to get started.
-
-
- @else
-
-
-
- |
- Name
- |
-
- Actions
- |
-
-
-
-
- @foreach($instances as $instance)
-
- |
- {{ $instance['name'] }}
- |
-
-
-
-
-
-
-
-
-
-
-
- |
-
- @endforeach
-
-
- @endif
-
- @foreach($instances as $instance)
-
-
-
Delete {{ $instance['name'] }}?
-
This will also remove this instance from your playlists.
-
-
-
-
-
- Cancel
-
- Delete instance
-
-
- @endforeach
-
-
-
diff --git a/resources/views/livewire/plugins/index.blade.php b/resources/views/livewire/plugins/index.blade.php
index 4347aaf..469365c 100644
--- a/resources/views/livewire/plugins/index.blade.php
+++ b/resources/views/livewire/plugins/index.blade.php
@@ -26,8 +26,6 @@ new class extends Component {
['name' => 'Markup', 'flux_icon_name' => 'code-bracket', 'detail_view_route' => 'plugins.markup'],
'api' =>
['name' => 'API', 'flux_icon_name' => 'braces', 'detail_view_route' => 'plugins.api'],
- 'image-webhook' =>
- ['name' => 'Image Webhook', 'flux_icon_name' => 'photo', 'detail_view_route' => 'plugins.image-webhook'],
];
protected $rules = [
@@ -42,12 +40,7 @@ new class extends Component {
public function refreshPlugins(): void
{
- // Only show recipe plugins in the main list (image_webhook has its own management page)
- $userPlugins = auth()->user()?->plugins()
- ->where('plugin_type', 'recipe')
- ->get()
- ->makeHidden(['render_markup', 'data_payload'])
- ->toArray();
+ $userPlugins = auth()->user()?->plugins?->makeHidden(['render_markup', 'data_payload'])->toArray();
$allPlugins = array_merge($this->native_plugins, $userPlugins ?? []);
$allPlugins = array_values($allPlugins);
$allPlugins = $this->sortPlugins($allPlugins);
diff --git a/resources/views/livewire/plugins/recipe.blade.php b/resources/views/livewire/plugins/recipe.blade.php
index bda8221..ec53aae 100644
--- a/resources/views/livewire/plugins/recipe.blade.php
+++ b/resources/views/livewire/plugins/recipe.blade.php
@@ -976,8 +976,6 @@ HTML;
wire:model.defer="multiValues.{{ $fieldKey }}.{{ $index }}"
:placeholder="$field['placeholder'] ?? 'Value...'"
class="flex-1"
- pattern="[^,]*"
- title="Commas are not allowed in this field"
/>
@if(count($multiValues[$fieldKey]) > 1)
diff --git a/routes/api.php b/routes/api.php
index 5700a43..b1d08b4 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -549,91 +549,6 @@ Route::post('custom_plugins/{plugin_uuid}', function (string $plugin_uuid) {
return response()->json(['message' => 'Data updated successfully']);
})->name('api.custom_plugins.webhook');
-Route::post('plugin_settings/{uuid}/image', function (Request $request, string $uuid) {
- $plugin = Plugin::where('uuid', $uuid)->firstOrFail();
-
- // Check if plugin is image_webhook type
- if ($plugin->plugin_type !== 'image_webhook') {
- return response()->json(['error' => 'Plugin is not an image webhook plugin'], 400);
- }
-
- // Accept image from either multipart form or raw binary
- $image = null;
- $extension = null;
-
- if ($request->hasFile('image')) {
- $file = $request->file('image');
- $extension = mb_strtolower($file->getClientOriginalExtension());
- $image = $file->get();
- } elseif ($request->has('image')) {
- // Base64 encoded image
- $imageData = $request->input('image');
- if (preg_match('/^data:image\/(\w+);base64,/', $imageData, $matches)) {
- $extension = mb_strtolower($matches[1]);
- $image = base64_decode(mb_substr($imageData, mb_strpos($imageData, ',') + 1));
- } else {
- return response()->json(['error' => 'Invalid image format. Expected base64 data URI.'], 400);
- }
- } else {
- // Try raw binary
- $image = $request->getContent();
- $contentType = $request->header('Content-Type', '');
- $trimmedContent = mb_trim($image);
-
- // Check if content is empty or just empty JSON
- if (empty($image) || $trimmedContent === '' || $trimmedContent === '{}') {
- return response()->json(['error' => 'No image data provided'], 400);
- }
-
- // If it's a JSON request without image field, return error
- if (str_contains($contentType, 'application/json')) {
- return response()->json(['error' => 'No image data provided'], 400);
- }
-
- // Detect image type from content
- $finfo = finfo_open(FILEINFO_MIME_TYPE);
- $mimeType = finfo_buffer($finfo, $image);
- finfo_close($finfo);
-
- $extension = match ($mimeType) {
- 'image/png' => 'png',
- 'image/bmp' => 'bmp',
- default => null,
- };
-
- if (! $extension) {
- return response()->json(['error' => 'Unsupported image format. Expected PNG or BMP.'], 400);
- }
- }
-
- // Validate extension
- $allowedExtensions = ['png', 'bmp'];
- if (! in_array($extension, $allowedExtensions)) {
- return response()->json(['error' => 'Unsupported image format. Expected PNG or BMP.'], 400);
- }
-
- // Generate a new UUID for each image upload to prevent device caching
- $imageUuid = \Illuminate\Support\Str::uuid()->toString();
- $filename = $imageUuid.'.'.$extension;
- $path = 'images/generated/'.$filename;
-
- // Save image to storage
- Storage::disk('public')->put($path, $image);
-
- // Update plugin's current_image field with the new UUID
- $plugin->update([
- 'current_image' => $imageUuid,
- ]);
-
- // Clean up old images
- ImageGenerationService::cleanupFolder();
-
- return response()->json([
- 'message' => 'Image uploaded successfully',
- 'image_url' => url('storage/'.$path),
- ]);
-})->name('api.plugin_settings.image');
-
Route::get('plugin_settings/{trmnlp_id}/archive', function (Request $request, string $trmnlp_id) {
if (! $trmnlp_id || mb_trim($trmnlp_id) === '') {
return response()->json([
diff --git a/routes/web.php b/routes/web.php
index b3069bd..7b7868d 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -31,8 +31,6 @@ Route::middleware(['auth'])->group(function () {
Volt::route('plugins/recipe/{plugin}', 'plugins.recipe')->name('plugins.recipe');
Volt::route('plugins/markup', 'plugins.markup')->name('plugins.markup');
Volt::route('plugins/api', 'plugins.api')->name('plugins.api');
- Volt::route('plugins/image-webhook', 'plugins.image-webhook')->name('plugins.image-webhook');
- Volt::route('plugins/image-webhook/{plugin}', 'plugins.image-webhook-instance')->name('plugins.image-webhook-instance');
Volt::route('playlists', 'playlists.index')->name('playlists.index');
Route::get('plugin_settings/{trmnlp_id}/edit', function (Request $request, string $trmnlp_id) {
diff --git a/tests/Feature/Api/ImageWebhookTest.php b/tests/Feature/Api/ImageWebhookTest.php
deleted file mode 100644
index 121f90a..0000000
--- a/tests/Feature/Api/ImageWebhookTest.php
+++ /dev/null
@@ -1,196 +0,0 @@
-makeDirectory('/images/generated');
-});
-
-test('can upload image to image webhook plugin via multipart form', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->imageWebhook()->create([
- 'user_id' => $user->id,
- ]);
-
- $image = UploadedFile::fake()->image('test.png', 800, 480);
-
- $response = $this->post("/api/plugin_settings/{$plugin->uuid}/image", [
- 'image' => $image,
- ]);
-
- $response->assertOk()
- ->assertJsonStructure([
- 'message',
- 'image_url',
- ]);
-
- $plugin->refresh();
- expect($plugin->current_image)
- ->not->toBeNull()
- ->not->toBe($plugin->uuid); // Should be a new UUID, not the plugin's UUID
-
- // File should exist with the new UUID
- Storage::disk('public')->assertExists("images/generated/{$plugin->current_image}.png");
-
- // Image URL should contain the new UUID
- expect($response->json('image_url'))
- ->toContain($plugin->current_image);
-});
-
-test('can upload image to image webhook plugin via raw binary', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->imageWebhook()->create([
- 'user_id' => $user->id,
- ]);
-
- // Create a simple PNG image binary
- $pngData = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
-
- $response = $this->call('POST', "/api/plugin_settings/{$plugin->uuid}/image", [], [], [], [
- 'CONTENT_TYPE' => 'image/png',
- ], $pngData);
-
- $response->assertOk()
- ->assertJsonStructure([
- 'message',
- 'image_url',
- ]);
-
- $plugin->refresh();
- expect($plugin->current_image)
- ->not->toBeNull()
- ->not->toBe($plugin->uuid); // Should be a new UUID, not the plugin's UUID
-
- // File should exist with the new UUID
- Storage::disk('public')->assertExists("images/generated/{$plugin->current_image}.png");
-
- // Image URL should contain the new UUID
- expect($response->json('image_url'))
- ->toContain($plugin->current_image);
-});
-
-test('can upload image to image webhook plugin via base64 data URI', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->imageWebhook()->create([
- 'user_id' => $user->id,
- ]);
-
- // Create a simple PNG image as base64 data URI
- $base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
-
- $response = $this->postJson("/api/plugin_settings/{$plugin->uuid}/image", [
- 'image' => $base64Image,
- ]);
-
- $response->assertOk()
- ->assertJsonStructure([
- 'message',
- 'image_url',
- ]);
-
- $plugin->refresh();
- expect($plugin->current_image)
- ->not->toBeNull()
- ->not->toBe($plugin->uuid); // Should be a new UUID, not the plugin's UUID
-
- // File should exist with the new UUID
- Storage::disk('public')->assertExists("images/generated/{$plugin->current_image}.png");
-
- // Image URL should contain the new UUID
- expect($response->json('image_url'))
- ->toContain($plugin->current_image);
-});
-
-test('returns 400 for non-image-webhook plugin', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'plugin_type' => 'recipe',
- ]);
-
- $image = UploadedFile::fake()->image('test.png', 800, 480);
-
- $response = $this->post("/api/plugin_settings/{$plugin->uuid}/image", [
- 'image' => $image,
- ]);
-
- $response->assertStatus(400)
- ->assertJson(['error' => 'Plugin is not an image webhook plugin']);
-});
-
-test('returns 404 for non-existent plugin', function (): void {
- $image = UploadedFile::fake()->image('test.png', 800, 480);
-
- $response = $this->post('/api/plugin_settings/'.Str::uuid().'/image', [
- 'image' => $image,
- ]);
-
- $response->assertNotFound();
-});
-
-test('returns 400 for unsupported image format', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->imageWebhook()->create([
- 'user_id' => $user->id,
- ]);
-
- // Create a fake GIF file (not supported)
- $gifData = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
-
- $response = $this->call('POST', "/api/plugin_settings/{$plugin->uuid}/image", [], [], [], [
- 'CONTENT_TYPE' => 'image/gif',
- ], $gifData);
-
- $response->assertStatus(400)
- ->assertJson(['error' => 'Unsupported image format. Expected PNG or BMP.']);
-});
-
-test('returns 400 for JPG image format', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->imageWebhook()->create([
- 'user_id' => $user->id,
- ]);
-
- // Create a fake JPG file (not supported)
- $jpgData = base64_decode('/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8A8A');
-
- $response = $this->call('POST', "/api/plugin_settings/{$plugin->uuid}/image", [], [], [], [
- 'CONTENT_TYPE' => 'image/jpeg',
- ], $jpgData);
-
- $response->assertStatus(400)
- ->assertJson(['error' => 'Unsupported image format. Expected PNG or BMP.']);
-});
-
-test('returns 400 when no image data provided', function (): void {
- $user = User::factory()->create();
- $plugin = Plugin::factory()->imageWebhook()->create([
- 'user_id' => $user->id,
- ]);
-
- $response = $this->postJson("/api/plugin_settings/{$plugin->uuid}/image", []);
-
- $response->assertStatus(400)
- ->assertJson(['error' => 'No image data provided']);
-});
-
-test('image webhook plugin isDataStale returns false', function (): void {
- $plugin = Plugin::factory()->imageWebhook()->create();
-
- expect($plugin->isDataStale())->toBeFalse();
-});
-
-test('image webhook plugin factory creates correct plugin type', function (): void {
- $plugin = Plugin::factory()->imageWebhook()->create();
-
- expect($plugin)
- ->plugin_type->toBe('image_webhook')
- ->data_strategy->toBe('static');
-});
diff --git a/tests/Unit/Models/PluginTest.php b/tests/Unit/Models/PluginTest.php
index 0847e36..b42668d 100644
--- a/tests/Unit/Models/PluginTest.php
+++ b/tests/Unit/Models/PluginTest.php
@@ -685,11 +685,11 @@ test('plugin render includes utc_offset and time_zone_iana in trmnl.user context
* [Input, Expected Result, Forbidden String]
*/
dataset('xss_vectors', [
- 'standard_script' => ['Safe ', 'Safe ', '', 'Safe ', '