');
-});
-
-it('imports plugin with only shared.blade.php file', function (): void {
- $user = User::factory()->create();
-
- $zipContent = createMockZipFile([
- 'src/settings.yml' => getValidSettingsYaml(),
- 'src/shared.blade.php' => '
',
- ]);
-
- $zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
-
- $pluginImportService = new PluginImportService();
- $plugin = $pluginImportService->importFromZip($zipFile, $user);
-
- expect($plugin)->toBeInstanceOf(Plugin::class)
- ->and($plugin->markup_language)->toBe('blade')
- ->and($plugin->render_markup)->toBe('
');
+ expect($plugin->markup_language)->toBe('blade');
});
// Helper methods
function createMockZipFile(array $files): string
{
$zip = new ZipArchive();
-
- $tempFileName = 'test_zip_'.uniqid().'.zip';
- $tempFile = Storage::path($tempFileName);
+ $tempFile = tempnam(sys_get_temp_dir(), 'test_zip_');
$zip->open($tempFile, ZipArchive::CREATE);
@@ -556,8 +147,7 @@ function createMockZipFile(array $files): string
$zip->close();
$content = file_get_contents($tempFile);
-
- Storage::delete($tempFileName);
+ unlink($tempFile);
return $content;
}
diff --git a/tests/Feature/PluginInlineTemplatesTest.php b/tests/Feature/PluginInlineTemplatesTest.php
index 76b29d7..fb35344 100644
--- a/tests/Feature/PluginInlineTemplatesTest.php
+++ b/tests/Feature/PluginInlineTemplatesTest.php
@@ -5,7 +5,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
-test('renders plugin with inline templates', function (): void {
+test('renders plugin with inline templates', function () {
$plugin = Plugin::factory()->create([
'name' => 'Test Plugin',
'markup_language' => 'liquid',
@@ -61,16 +61,16 @@ LIQUID
// Should render both templates
// Check for any of the facts (since random number generation is non-deterministic)
$this->assertTrue(
- str_contains((string) $result, 'Fact 1') ||
- str_contains((string) $result, 'Fact 2') ||
- str_contains((string) $result, 'Fact 3')
+ str_contains($result, 'Fact 1') ||
+ str_contains($result, 'Fact 2') ||
+ str_contains($result, 'Fact 3')
);
$this->assertStringContainsString('Test Plugin', $result);
$this->assertStringContainsString('Please try to enjoy each fact equally', $result);
$this->assertStringContainsString('class="view view--full"', $result);
});
-test('renders plugin with inline templates using with syntax', function (): void {
+test('renders plugin with inline templates using with syntax', function () {
$plugin = Plugin::factory()->create([
'name' => 'Test Plugin',
'markup_language' => 'liquid',
@@ -127,16 +127,16 @@ LIQUID
// Should render both templates
// Check for any of the facts (since random number generation is non-deterministic)
$this->assertTrue(
- str_contains((string) $result, 'Fact 1') ||
- str_contains((string) $result, 'Fact 2') ||
- str_contains((string) $result, 'Fact 3')
+ str_contains($result, 'Fact 1') ||
+ str_contains($result, 'Fact 2') ||
+ str_contains($result, 'Fact 3')
);
$this->assertStringContainsString('Test Plugin', $result);
$this->assertStringContainsString('Please try to enjoy each fact equally', $result);
$this->assertStringContainsString('class="view view--full"', $result);
});
-test('renders plugin with simple inline template', function (): void {
+test('renders plugin with simple inline template', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
@@ -162,7 +162,7 @@ LIQUID
$this->assertStringContainsString('class="simple"', $result);
});
-test('renders plugin with liquid filter find_by', function (): void {
+test('renders plugin with liquid filter find_by', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
@@ -194,7 +194,7 @@ LIQUID
$this->assertStringContainsString('class="user"', $result);
});
-test('renders plugin with liquid filter find_by and fallback', function (): void {
+test('renders plugin with liquid filter find_by and fallback', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
@@ -216,7 +216,7 @@ LIQUID
$this->assertStringContainsString('Not Found', $result);
});
-test('renders plugin with liquid filter group_by', function (): void {
+test('renders plugin with liquid filter group_by', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
diff --git a/tests/Feature/PluginLiquidFilterTest.php b/tests/Feature/PluginLiquidFilterTest.php
index e6272c7..fb429ae 100644
--- a/tests/Feature/PluginLiquidFilterTest.php
+++ b/tests/Feature/PluginLiquidFilterTest.php
@@ -14,7 +14,7 @@ use Keepsuit\Liquid\Environment;
* to:
* {% assign _temp_xxx = collection | filter: "key", "value" %}{% for item in _temp_xxx %}
*/
-test('where filter works when assigned to variable first', function (): void {
+test('where filter works when assigned to variable first', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
@@ -29,6 +29,7 @@ assign collection = json_string | parse_json
{{ tide | json }}
{%- endfor %}
LIQUID
+ ,
]);
$result = $plugin->render('full');
@@ -41,7 +42,7 @@ LIQUID
$this->assertStringNotContainsString('"type":"L"', $result);
});
-test('where filter works directly in for loop with preprocessing', function (): void {
+test('where filter works directly in for loop with preprocessing', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
@@ -54,6 +55,7 @@ assign collection = json_string | parse_json
{{ tide | json }}
{%- endfor %}
LIQUID
+ ,
]);
$result = $plugin->render('full');
@@ -66,7 +68,7 @@ LIQUID
$this->assertStringNotContainsString('"type":"L"', $result);
});
-test('where filter works directly in for loop with multiple matches', function (): void {
+test('where filter works directly in for loop with multiple matches', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
@@ -79,6 +81,7 @@ assign collection = json_string | parse_json
{{ tide | json }}
{%- endfor %}
LIQUID
+ ,
]);
$result = $plugin->render('full');
@@ -92,7 +95,7 @@ LIQUID
$this->assertStringNotContainsString('"type":"L"', $result);
});
-it('encodes arrays for url_encode as JSON with spaces after commas and then percent-encodes', function (): void {
+it('encodes arrays for url_encode as JSON with spaces after commas and then percent-encodes', function () {
/** @var Environment $env */
$env = app('liquid.environment');
$env->filterRegistry->register(StandardFilters::class);
@@ -106,7 +109,7 @@ it('encodes arrays for url_encode as JSON with spaces after commas and then perc
expect($output)->toBe('%5B%22common%22%2C%22obscure%22%5D');
});
-it('keeps scalar url_encode behavior intact', function (): void {
+it('keeps scalar url_encode behavior intact', function () {
/** @var Environment $env */
$env = app('liquid.environment');
$env->filterRegistry->register(StandardFilters::class);
@@ -119,58 +122,3 @@ it('keeps scalar url_encode behavior intact', function (): void {
expect($output)->toBe('hello+world');
});
-
-test('where_exp filter works in liquid template', function (): void {
- $plugin = Plugin::factory()->create([
- 'markup_language' => 'liquid',
- 'render_markup' => <<<'LIQUID'
-{% liquid
-assign nums = "1, 2, 3, 4, 5" | split: ", " | map_to_i
-assign filtered = nums | where_exp: "n", "n >= 3"
-%}
-
-{% for num in filtered %}
- {{ num }}
-{%- endfor %}
-LIQUID
- ]);
-
- $result = $plugin->render('full');
-
- // Debug: Let's see what the actual output is
- // The issue might be that the HTML contains "1" in other places
- // Let's check if the filtered numbers are actually in the content
- $this->assertStringContainsString('3', $result);
- $this->assertStringContainsString('4', $result);
- $this->assertStringContainsString('5', $result);
-
- // Instead of checking for absence of 1 and 2, let's verify the count
- // The filtered result should only contain 3, 4, 5
- $filteredContent = strip_tags((string) $result);
- $this->assertStringNotContainsString('1', $filteredContent);
- $this->assertStringNotContainsString('2', $filteredContent);
-});
-
-test('where_exp filter works with object properties', function (): void {
- $plugin = Plugin::factory()->create([
- 'markup_language' => 'liquid',
- 'render_markup' => <<<'LIQUID'
-{% liquid
-assign users = '[{"name":"Alice","age":25},{"name":"Bob","age":30},{"name":"Charlie","age":35}]' | parse_json
-assign adults = users | where_exp: "user", "user.age >= 30"
-%}
-
-{% for user in adults %}
- {{ user.name }} ({{ user.age }})
-{%- endfor %}
-LIQUID
- ]);
-
- $result = $plugin->render('full');
-
- // Should output users >= 30
- $this->assertStringContainsString('Bob (30)', $result);
- $this->assertStringContainsString('Charlie (35)', $result);
- // Should not contain users < 30
- $this->assertStringNotContainsString('Alice (25)', $result);
-});
diff --git a/tests/Feature/PluginRequiredConfigurationTest.php b/tests/Feature/PluginRequiredConfigurationTest.php
index 51e1b76..552b996 100644
--- a/tests/Feature/PluginRequiredConfigurationTest.php
+++ b/tests/Feature/PluginRequiredConfigurationTest.php
@@ -6,7 +6,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
-test('hasMissingRequiredConfigurationFields returns true when required field is null', function (): void {
+test('hasMissingRequiredConfigurationFields returns true when required field is null', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -39,7 +39,7 @@ test('hasMissingRequiredConfigurationFields returns true when required field is
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
});
-test('hasMissingRequiredConfigurationFields returns false when all required fields are set', function (): void {
+test('hasMissingRequiredConfigurationFields returns false when all required fields are set', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -73,7 +73,7 @@ test('hasMissingRequiredConfigurationFields returns false when all required fiel
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
});
-test('hasMissingRequiredConfigurationFields returns false when no custom fields exist', function (): void {
+test('hasMissingRequiredConfigurationFields returns false when no custom fields exist', function () {
$user = User::factory()->create();
$plugin = Plugin::factory()->create([
@@ -85,7 +85,7 @@ test('hasMissingRequiredConfigurationFields returns false when no custom fields
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
});
-test('hasMissingRequiredConfigurationFields returns true when explicitly required field is null', function (): void {
+test('hasMissingRequiredConfigurationFields returns true when explicitly required field is null', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -111,7 +111,7 @@ test('hasMissingRequiredConfigurationFields returns true when explicitly require
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
});
-test('hasMissingRequiredConfigurationFields returns true when required field is empty string', function (): void {
+test('hasMissingRequiredConfigurationFields returns true when required field is empty string', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -137,7 +137,7 @@ test('hasMissingRequiredConfigurationFields returns true when required field is
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
});
-test('hasMissingRequiredConfigurationFields returns true when required array field is empty', function (): void {
+test('hasMissingRequiredConfigurationFields returns true when required array field is empty', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -164,7 +164,7 @@ test('hasMissingRequiredConfigurationFields returns true when required array fie
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
});
-test('hasMissingRequiredConfigurationFields returns false when author_bio field is present but other required field is set', function (): void {
+test('hasMissingRequiredConfigurationFields returns false when author_bio field is present but other required field is set', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -193,7 +193,7 @@ test('hasMissingRequiredConfigurationFields returns false when author_bio field
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
});
-test('hasMissingRequiredConfigurationFields returns false when field has default value', function (): void {
+test('hasMissingRequiredConfigurationFields returns false when field has default value', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -217,7 +217,7 @@ test('hasMissingRequiredConfigurationFields returns false when field has default
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
});
-test('hasMissingRequiredConfigurationFields returns true when required xhrSelect field is missing', function (): void {
+test('hasMissingRequiredConfigurationFields returns true when required xhrSelect field is missing', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -242,7 +242,7 @@ test('hasMissingRequiredConfigurationFields returns true when required xhrSelect
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
});
-test('hasMissingRequiredConfigurationFields returns false when required xhrSelect field is set', function (): void {
+test('hasMissingRequiredConfigurationFields returns false when required xhrSelect field is set', function () {
$user = User::factory()->create();
$configurationTemplate = [
@@ -268,79 +268,3 @@ test('hasMissingRequiredConfigurationFields returns false when required xhrSelec
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
});
-
-test('hasMissingRequiredConfigurationFields returns true when required multi_string field is missing', function (): void {
- $user = User::factory()->create();
-
- $configurationTemplate = [
- 'custom_fields' => [
- [
- 'keyname' => 'tags',
- 'field_type' => 'multi_string',
- 'name' => 'Tags',
- 'description' => 'Enter tags separated by commas',
- // Not marked as optional, so it's required
- ],
- ],
- ];
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'configuration_template' => $configurationTemplate,
- 'configuration' => [], // Empty configuration
- ]);
-
- expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
-});
-
-test('hasMissingRequiredConfigurationFields returns false when required multi_string field is set', function (): void {
- $user = User::factory()->create();
-
- $configurationTemplate = [
- 'custom_fields' => [
- [
- 'keyname' => 'tags',
- 'field_type' => 'multi_string',
- 'name' => 'Tags',
- 'description' => 'Enter tags separated by commas',
- // Not marked as optional, so it's required
- ],
- ],
- ];
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'configuration_template' => $configurationTemplate,
- 'configuration' => [
- 'tags' => 'tag1, tag2, tag3', // Required field is set with comma-separated values
- ],
- ]);
-
- expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
-});
-
-test('hasMissingRequiredConfigurationFields returns true when required multi_string field is empty string', function (): void {
- $user = User::factory()->create();
-
- $configurationTemplate = [
- 'custom_fields' => [
- [
- 'keyname' => 'tags',
- 'field_type' => 'multi_string',
- 'name' => 'Tags',
- 'description' => 'Enter tags separated by commas',
- // Not marked as optional, so it's required
- ],
- ],
- ];
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'configuration_template' => $configurationTemplate,
- 'configuration' => [
- 'tags' => '', // Empty string
- ],
- ]);
-
- expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
-});
diff --git a/tests/Feature/PluginResponseTest.php b/tests/Feature/PluginResponseTest.php
deleted file mode 100644
index 2a75c9e..0000000
--- a/tests/Feature/PluginResponseTest.php
+++ /dev/null
@@ -1,287 +0,0 @@
- Http::response([
- 'title' => 'Test Data',
- 'items' => [
- ['id' => 1, 'name' => 'Item 1'],
- ['id' => 2, 'name' => 'Item 2'],
- ],
- ], 200, ['Content-Type' => 'application/json']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/api/data',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toBe([
- 'title' => 'Test Data',
- 'items' => [
- ['id' => 1, 'name' => 'Item 1'],
- ['id' => 2, 'name' => 'Item 2'],
- ],
- ]);
-});
-
-test('plugin parses XML responses and wraps under rss key', function (): void {
- $xmlContent = '
-
-
- Test RSS Feed
- -
- Test Item 1
- Description 1
-
- -
- Test Item 2
- Description 2
-
-
- ';
-
- Http::fake([
- 'example.com/feed.xml' => Http::response($xmlContent, 200, ['Content-Type' => 'application/xml']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/feed.xml',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toHaveKey('rss');
- expect($plugin->data_payload['rss'])->toHaveKey('@attributes');
- expect($plugin->data_payload['rss'])->toHaveKey('channel');
- expect($plugin->data_payload['rss']['channel']['title'])->toBe('Test RSS Feed');
- expect($plugin->data_payload['rss']['channel']['item'])->toHaveCount(2);
-});
-
-test('plugin parses JSON-parsable response body as JSON', function (): void {
- $jsonContent = '{"title": "Test Data", "items": [1, 2, 3]}';
-
- Http::fake([
- 'example.com/data' => Http::response($jsonContent, 200, ['Content-Type' => 'text/plain']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/data',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toBe([
- 'title' => 'Test Data',
- 'items' => [1, 2, 3],
- ]);
-});
-
-test('plugin wraps plain text response body as JSON', function (): void {
- $jsonContent = 'Lorem ipsum dolor sit amet';
-
- Http::fake([
- 'example.com/data' => Http::response($jsonContent, 200, ['Content-Type' => 'text/plain']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/data',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toBe([
- 'data' => 'Lorem ipsum dolor sit amet',
- ]);
-});
-
-test('plugin handles invalid XML gracefully', function (): void {
- $invalidXml = '
- unclosed tag';
-
- Http::fake([
- 'example.com/invalid.xml' => Http::response($invalidXml, 200, ['Content-Type' => 'application/xml']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/invalid.xml',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toBe(['error' => 'Failed to parse XML response']);
-});
-
-test('plugin handles multiple URLs with mixed content types', function (): void {
- $jsonResponse = ['title' => 'JSON Data', 'items' => [1, 2, 3]];
- $xmlContent = '
- XML Data
';
-
- Http::fake([
- 'example.com/json' => Http::response($jsonResponse, 200, ['Content-Type' => 'application/json']),
- 'example.com/xml' => Http::response($xmlContent, 200, ['Content-Type' => 'application/xml']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => "https://example.com/json\nhttps://example.com/xml",
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toHaveKey('IDX_0');
- expect($plugin->data_payload)->toHaveKey('IDX_1');
-
- // First URL should be JSON
- expect($plugin->data_payload['IDX_0'])->toBe($jsonResponse);
-
- // Second URL should be XML wrapped under rss
- expect($plugin->data_payload['IDX_1'])->toHaveKey('rss');
- expect($plugin->data_payload['IDX_1']['rss']['item'])->toBe('XML Data');
-});
-
-test('plugin handles POST requests with XML responses', function (): void {
- $xmlContent = 'successtest';
-
- Http::fake([
- 'example.com/api' => Http::response($xmlContent, 200, ['Content-Type' => 'application/xml']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/api',
- 'polling_verb' => 'post',
- 'polling_body' => '{"query": "test"}',
- ]);
-
- $plugin->updateDataPayload();
-
- $plugin->refresh();
-
- expect($plugin->data_payload)->toHaveKey('rss');
- expect($plugin->data_payload['rss'])->toHaveKey('status');
- expect($plugin->data_payload['rss'])->toHaveKey('data');
- expect($plugin->data_payload['rss']['status'])->toBe('success');
- expect($plugin->data_payload['rss']['data'])->toBe('test');
-});
-
-test('plugin parses iCal responses and filters to recent window', function (): void {
- Carbon::setTestNow(Carbon::parse('2025-01-15 12:00:00', 'UTC'));
-
- $icalContent = <<<'ICS'
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Example Corp.//CalDAV Client//EN
-BEGIN:VEVENT
-UID:event-1@example.com
-DTSTAMP:20250101T120000Z
-DTSTART:20250110T090000Z
-DTEND:20250110T100000Z
-SUMMARY:Past within window
-END:VEVENT
-BEGIN:VEVENT
-UID:event-2@example.com
-DTSTAMP:20250101T120000Z
-DTSTART:20250301T090000Z
-DTEND:20250301T100000Z
-SUMMARY:Far future
-END:VEVENT
-BEGIN:VEVENT
-UID:event-3@example.com
-DTSTAMP:20250101T120000Z
-DTSTART:20250120T090000Z
-DTEND:20250120T100000Z
-SUMMARY:Upcoming within window
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- Http::fake([
- 'example.com/calendar.ics' => Http::response($icalContent, 200, ['Content-Type' => 'text/calendar']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/calendar.ics',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
- $plugin->refresh();
-
- $ical = $plugin->data_payload['ical'];
-
- expect($ical)->toHaveCount(2);
- expect($ical[0]['SUMMARY'])->toBe('Past within window');
- expect($ical[1]['SUMMARY'])->toBe('Upcoming within window');
- expect(collect($ical)->pluck('SUMMARY'))->not->toContain('Far future');
- expect($ical[0]['DTSTART'])->toBe('2025-01-10T09:00:00+00:00');
- expect($ical[1]['DTSTART'])->toBe('2025-01-20T09:00:00+00:00');
-
- Carbon::setTestNow();
-});
-
-test('plugin detects iCal content without calendar content type', function (): void {
- Carbon::setTestNow(Carbon::parse('2025-01-15 12:00:00', 'UTC'));
-
- $icalContent = <<<'ICS'
-BEGIN:VCALENDAR
-VERSION:2.0
-BEGIN:VEVENT
-UID:event-body-detected@example.com
-DTSTAMP:20250101T120000Z
-DTSTART:20250116T090000Z
-DTEND:20250116T100000Z
-SUMMARY:Detected by body
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- Http::fake([
- 'example.com/calendar-body.ics' => Http::response($icalContent, 200, ['Content-Type' => 'text/plain']),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => 'https://example.com/calendar-body.ics',
- 'polling_verb' => 'get',
- ]);
-
- $plugin->updateDataPayload();
- $plugin->refresh();
-
- expect($plugin->data_payload)->toHaveKey('ical');
- expect($plugin->data_payload['ical'])->toHaveCount(1);
- expect($plugin->data_payload['ical'][0]['SUMMARY'])->toBe('Detected by body');
- expect($plugin->data_payload['ical'][0]['DTSTART'])->toBe('2025-01-16T09:00:00+00:00');
-
- Carbon::setTestNow();
-});
diff --git a/tests/Feature/PluginWebhookTest.php b/tests/Feature/PluginWebhookTest.php
index 22d1d54..70fa53a 100644
--- a/tests/Feature/PluginWebhookTest.php
+++ b/tests/Feature/PluginWebhookTest.php
@@ -3,7 +3,7 @@
use App\Models\Plugin;
use Illuminate\Support\Str;
-test('webhook updates plugin data for webhook strategy', function (): void {
+test('webhook updates plugin data for webhook strategy', function () {
// Create a plugin with webhook strategy
$plugin = Plugin::factory()->create([
'data_strategy' => 'webhook',
@@ -26,7 +26,7 @@ test('webhook updates plugin data for webhook strategy', function (): void {
]);
});
-test('webhook returns 400 for non-webhook strategy plugins', function (): void {
+test('webhook returns 400 for non-webhook strategy plugins', function () {
// Create a plugin with non-webhook strategy
$plugin = Plugin::factory()->create([
'data_strategy' => 'polling',
@@ -43,7 +43,7 @@ test('webhook returns 400 for non-webhook strategy plugins', function (): void {
->assertJson(['error' => 'Plugin does not use webhook strategy']);
});
-test('webhook returns 400 when merge_variables is missing', function (): void {
+test('webhook returns 400 when merge_variables is missing', function () {
// Create a plugin with webhook strategy
$plugin = Plugin::factory()->create([
'data_strategy' => 'webhook',
@@ -58,7 +58,7 @@ test('webhook returns 400 when merge_variables is missing', function (): void {
->assertJson(['error' => 'Request must contain merge_variables key']);
});
-test('webhook returns 404 for non-existent plugin', function (): void {
+test('webhook returns 404 for non-existent plugin', function () {
// Make request with non-existent plugin UUID
$response = $this->postJson('/api/custom_plugins/'.Str::uuid(), [
'merge_variables' => ['new' => 'data'],
diff --git a/tests/Feature/Settings/PasswordUpdateTest.php b/tests/Feature/Settings/PasswordUpdateTest.php
index 0e33955..3252860 100644
--- a/tests/Feature/Settings/PasswordUpdateTest.php
+++ b/tests/Feature/Settings/PasswordUpdateTest.php
@@ -6,7 +6,7 @@ use Livewire\Volt\Volt;
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
-test('password can be updated', function (): void {
+test('password can be updated', function () {
$user = User::factory()->create([
'password' => Hash::make('password'),
]);
@@ -24,7 +24,7 @@ test('password can be updated', function (): void {
expect(Hash::check('new-password', $user->refresh()->password))->toBeTrue();
});
-test('correct password must be provided to update password', function (): void {
+test('correct password must be provided to update password', function () {
$user = User::factory()->create([
'password' => Hash::make('password'),
]);
diff --git a/tests/Feature/Settings/ProfileUpdateTest.php b/tests/Feature/Settings/ProfileUpdateTest.php
index cbf424c..48ea114 100644
--- a/tests/Feature/Settings/ProfileUpdateTest.php
+++ b/tests/Feature/Settings/ProfileUpdateTest.php
@@ -5,13 +5,13 @@ use Livewire\Volt\Volt;
uses(Illuminate\Foundation\Testing\RefreshDatabase::class);
-test('profile page is displayed', function (): void {
+test('profile page is displayed', function () {
$this->actingAs($user = User::factory()->create());
$this->get('/settings/profile')->assertOk();
});
-test('profile information can be updated', function (): void {
+test('profile information can be updated', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -30,7 +30,7 @@ test('profile information can be updated', function (): void {
expect($user->email_verified_at)->toBeNull();
});
-test('email verification status is unchanged when email address is unchanged', function (): void {
+test('email verification status is unchanged when email address is unchanged', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -45,7 +45,7 @@ test('email verification status is unchanged when email address is unchanged', f
expect($user->refresh()->email_verified_at)->not->toBeNull();
});
-test('user can delete their account', function (): void {
+test('user can delete their account', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -62,7 +62,7 @@ test('user can delete their account', function (): void {
expect(auth()->check())->toBeFalse();
});
-test('correct password must be provided to delete account', function (): void {
+test('correct password must be provided to delete account', function () {
$user = User::factory()->create();
$this->actingAs($user);
diff --git a/tests/Feature/TransformDefaultImagesTest.php b/tests/Feature/TransformDefaultImagesTest.php
deleted file mode 100644
index 2ea995f..0000000
--- a/tests/Feature/TransformDefaultImagesTest.php
+++ /dev/null
@@ -1,87 +0,0 @@
-makeDirectory('/images/default-screens');
- Storage::disk('public')->makeDirectory('/images/generated');
-
- // Create fallback image files that the service expects
- Storage::disk('public')->put('/images/setup-logo.bmp', 'fake-bmp-content');
- Storage::disk('public')->put('/images/sleep.bmp', 'fake-bmp-content');
-});
-
-test('command transforms default images for all device models', function (): void {
- // Ensure we have device models
- $deviceModels = DeviceModel::all();
- expect($deviceModels)->not->toBeEmpty();
-
- // Run the command
- $this->artisan('images:generate-defaults')
- ->assertExitCode(0);
-
- // Check that the default-screens directory was created
- expect(Storage::disk('public')->exists('images/default-screens'))->toBeTrue();
-
- // Check that images were generated for each device model
- foreach ($deviceModels as $deviceModel) {
- $extension = $deviceModel->mime_type === 'image/bmp' ? 'bmp' : 'png';
- $filename = "{$deviceModel->width}_{$deviceModel->height}_{$deviceModel->bit_depth}_{$deviceModel->rotation}.{$extension}";
-
- $setupPath = "images/default-screens/setup-logo_{$filename}";
- $sleepPath = "images/default-screens/sleep_{$filename}";
-
- expect(Storage::disk('public')->exists($setupPath))->toBeTrue();
- expect(Storage::disk('public')->exists($sleepPath))->toBeTrue();
- }
-});
-
-test('getDeviceSpecificDefaultImage falls back to original images for device without model', function (): void {
- $device = new Device();
- $device->deviceModel = null;
-
- $setupImage = ImageGenerationService::getDeviceSpecificDefaultImage($device, 'setup-logo');
- $sleepImage = ImageGenerationService::getDeviceSpecificDefaultImage($device, 'sleep');
-
- expect($setupImage)->toBe('images/setup-logo.bmp');
- expect($sleepImage)->toBe('images/sleep.bmp');
-});
-
-test('generateDefaultScreenImage creates images from Blade templates', function (): void {
- $device = Device::factory()->create();
-
- $setupUuid = ImageGenerationService::generateDefaultScreenImage($device, 'setup-logo');
- $sleepUuid = ImageGenerationService::generateDefaultScreenImage($device, 'sleep');
-
- expect($setupUuid)->not->toBeEmpty();
- expect($sleepUuid)->not->toBeEmpty();
- expect($setupUuid)->not->toBe($sleepUuid);
-
- // Check that the generated images exist
- $setupPath = "images/generated/{$setupUuid}.png";
- $sleepPath = "images/generated/{$sleepUuid}.png";
-
- expect(Storage::disk('public')->exists($setupPath))->toBeTrue();
- expect(Storage::disk('public')->exists($sleepPath))->toBeTrue();
-})->skipOnCI();
-
-test('generateDefaultScreenImage throws exception for invalid image type', function (): void {
- $device = Device::factory()->create();
-
- expect(fn (): string => ImageGenerationService::generateDefaultScreenImage($device, 'invalid-type'))
- ->toThrow(InvalidArgumentException::class);
-});
-
-test('getDeviceSpecificDefaultImage returns null for invalid image type', function (): void {
- $device = new Device();
- $device->deviceModel = DeviceModel::first();
-
- $result = ImageGenerationService::getDeviceSpecificDefaultImage($device, 'invalid-type');
- expect($result)->toBeNull();
-});
diff --git a/tests/Feature/Volt/CatalogTrmnlTest.php b/tests/Feature/Volt/CatalogTrmnlTest.php
deleted file mode 100644
index a80c63a..0000000
--- a/tests/Feature/Volt/CatalogTrmnlTest.php
+++ /dev/null
@@ -1,286 +0,0 @@
- Http::response([
- 'data' => [
- [
- 'id' => 123,
- 'name' => 'Weather Chum',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 10, 'forks' => 2],
- ],
- ],
- ], 200),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Weather Chum')
- ->assertSee('Install')
- ->assertDontSeeHtml('variant="subtle" icon="eye"')
- ->assertSee('Installs: 10');
-});
-
-it('shows preview button when screenshot_url is provided', function (): void {
- Http::fake([
- 'usetrmnl.com/recipes.json*' => Http::response([
- 'data' => [
- [
- 'id' => 123,
- 'name' => 'Weather Chum',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => 'https://example.com/screenshot.png',
- 'author_bio' => null,
- 'stats' => ['installs' => 10, 'forks' => 2],
- ],
- ],
- ], 200),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Weather Chum')
- ->assertSee('Preview');
-});
-
-it('searches TRMNL recipes when search term is provided', function (): void {
- Http::fake([
- // First call (mount -> newest)
- 'usetrmnl.com/recipes.json?*' => Http::sequence()
- ->push([
- 'data' => [
- [
- 'id' => 1,
- 'name' => 'Initial Recipe',
- 'icon_url' => null,
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 1, 'forks' => 0],
- ],
- ],
- ], 200)
- // Second call (search)
- ->push([
- 'data' => [
- [
- 'id' => 2,
- 'name' => 'Weather Search Result',
- 'icon_url' => null,
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 3, 'forks' => 1],
- ],
- ],
- ], 200),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Initial Recipe')
- ->set('search', 'weather')
- ->assertSee('Weather Search Result')
- ->assertSee('Install');
-});
-
-it('installs plugin successfully when user is authenticated', function (): void {
- $user = User::factory()->create();
-
- Http::fake([
- 'usetrmnl.com/recipes.json*' => Http::response([
- 'data' => [
- [
- 'id' => 123,
- 'name' => 'Weather Chum',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 10, 'forks' => 2],
- ],
- ],
- ], 200),
- 'usetrmnl.com/api/plugin_settings/123/archive*' => Http::response('fake zip content', 200),
- ]);
-
- $this->actingAs($user);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Weather Chum')
- ->call('installPlugin', '123')
- ->assertSee('Error installing plugin'); // This will fail because we don't have a real zip file
-});
-
-it('shows error when user is not authenticated', function (): void {
- Http::fake([
- 'usetrmnl.com/recipes.json*' => Http::response([
- 'data' => [
- [
- 'id' => 123,
- 'name' => 'Weather Chum',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 10, 'forks' => 2],
- ],
- ],
- ], 200),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Weather Chum')
- ->call('installPlugin', '123')
- ->assertStatus(403); // This will return 403 because user is not authenticated
-});
-
-it('shows error when plugin installation fails', function (): void {
- $user = User::factory()->create();
-
- Http::fake([
- 'usetrmnl.com/recipes.json*' => Http::response([
- 'data' => [
- [
- 'id' => 123,
- 'name' => 'Weather Chum',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 10, 'forks' => 2],
- ],
- ],
- ], 200),
- 'usetrmnl.com/api/plugin_settings/123/archive*' => Http::response('invalid zip content', 200),
- ]);
-
- $this->actingAs($user);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Weather Chum')
- ->call('installPlugin', '123')
- ->assertSee('Error installing plugin'); // This will fail because the zip content is invalid
-});
-
-it('previews a recipe with async fetch', function (): void {
- Http::fake([
- 'usetrmnl.com/recipes.json*' => Http::response([
- 'data' => [
- [
- 'id' => 123,
- 'name' => 'Weather Chum',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => 'https://example.com/old.png',
- 'author_bio' => null,
- 'stats' => ['installs' => 10, 'forks' => 2],
- ],
- ],
- ], 200),
- 'usetrmnl.com/recipes/123.json' => Http::response([
- 'data' => [
- 'id' => 123,
- 'name' => 'Weather Chum Updated',
- 'icon_url' => 'https://example.com/icon.png',
- 'screenshot_url' => 'https://example.com/new.png',
- 'author_bio' => ['description' => 'New bio'],
- 'stats' => ['installs' => 11, 'forks' => 3],
- ],
- ], 200),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Weather Chum')
- ->call('previewRecipe', '123')
- ->assertSet('previewingRecipe', '123')
- ->assertSet('previewData.name', 'Weather Chum Updated')
- ->assertSet('previewData.screenshot_url', 'https://example.com/new.png')
- ->assertSee('Preview Weather Chum Updated')
- ->assertSee('New bio');
-});
-
-it('supports pagination and loading more recipes', function (): void {
- Http::fake([
- 'usetrmnl.com/recipes.json?sort-by=newest&page=1' => Http::response([
- 'data' => [
- [
- 'id' => 1,
- 'name' => 'Recipe Page 1',
- 'icon_url' => null,
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 1, 'forks' => 0],
- ],
- ],
- 'next_page_url' => '/recipes.json?page=2',
- ], 200),
- 'usetrmnl.com/recipes.json?sort-by=newest&page=2' => Http::response([
- 'data' => [
- [
- 'id' => 2,
- 'name' => 'Recipe Page 2',
- 'icon_url' => null,
- 'screenshot_url' => null,
- 'author_bio' => null,
- 'stats' => ['installs' => 2, 'forks' => 0],
- ],
- ],
- 'next_page_url' => null,
- ], 200),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Recipe Page 1')
- ->assertDontSee('Recipe Page 2')
- ->assertSee('Load next page')
- ->call('loadMore')
- ->assertSee('Recipe Page 1')
- ->assertSee('Recipe Page 2')
- ->assertDontSee('Load next page');
-});
-
-it('resets pagination when search term changes', function (): void {
- Http::fake([
- 'usetrmnl.com/recipes.json?sort-by=newest&page=1' => Http::sequence()
- ->push([
- 'data' => [['id' => 1, 'name' => 'Initial 1']],
- 'next_page_url' => '/recipes.json?page=2',
- ])
- ->push([
- 'data' => [['id' => 3, 'name' => 'Initial 1 Again']],
- 'next_page_url' => null,
- ]),
- 'usetrmnl.com/recipes.json?search=weather&sort-by=newest&page=1' => Http::response([
- 'data' => [['id' => 2, 'name' => 'Weather Result']],
- 'next_page_url' => null,
- ]),
- ]);
-
- Livewire::withoutLazyLoading();
-
- Volt::test('catalog.trmnl')
- ->assertSee('Initial 1')
- ->call('loadMore')
- ->set('search', 'weather')
- ->assertSee('Weather Result')
- ->assertDontSee('Initial 1')
- ->assertSet('page', 1);
-});
diff --git a/tests/Feature/Volt/DevicePalettesTest.php b/tests/Feature/Volt/DevicePalettesTest.php
deleted file mode 100644
index 376a4a6..0000000
--- a/tests/Feature/Volt/DevicePalettesTest.php
+++ /dev/null
@@ -1,575 +0,0 @@
-create();
-
- $this->actingAs($user);
-
- $this->get(route('device-palettes.index'))->assertOk();
-});
-
-test('component loads all device palettes on mount', function (): void {
- $user = User::factory()->create();
- $initialCount = DevicePalette::count();
- DevicePalette::create(['name' => 'palette-1', 'grays' => 2, 'framework_class' => '']);
- DevicePalette::create(['name' => 'palette-2', 'grays' => 4, 'framework_class' => '']);
- DevicePalette::create(['name' => 'palette-3', 'grays' => 16, 'framework_class' => '']);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index');
-
- $palettes = $component->get('devicePalettes');
- expect($palettes)->toHaveCount($initialCount + 3);
-});
-
-test('can open modal to create new device palette', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal');
-
- $component
- ->assertSet('editingDevicePaletteId', null)
- ->assertSet('viewingDevicePaletteId', null)
- ->assertSet('name', null)
- ->assertSet('grays', 2);
-});
-
-test('can create a new device palette', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('description', 'Test Palette Description')
- ->set('grays', 16)
- ->set('colors', ['#FF0000', '#00FF00'])
- ->set('framework_class', 'TestFramework')
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-
- expect(DevicePalette::where('name', 'test-palette')->exists())->toBeTrue();
-
- $palette = DevicePalette::where('name', 'test-palette')->first();
- expect($palette->description)->toBe('Test Palette Description');
- expect($palette->grays)->toBe(16);
- expect($palette->colors)->toBe(['#FF0000', '#00FF00']);
- expect($palette->framework_class)->toBe('TestFramework');
- expect($palette->source)->toBe('manual');
-});
-
-test('can create a grayscale-only palette without colors', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'grayscale-palette')
- ->set('grays', 256)
- ->set('colors', [])
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-
- $palette = DevicePalette::where('name', 'grayscale-palette')->first();
- expect($palette->colors)->toBeNull();
- expect($palette->grays)->toBe(256);
-});
-
-test('can open modal to edit existing device palette', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'existing-palette',
- 'description' => 'Existing Description',
- 'grays' => 4,
- 'colors' => ['#FF0000', '#00FF00'],
- 'framework_class' => 'Framework',
- 'source' => 'manual',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal', $palette->id);
-
- $component
- ->assertSet('editingDevicePaletteId', $palette->id)
- ->assertSet('name', 'existing-palette')
- ->assertSet('description', 'Existing Description')
- ->assertSet('grays', 4)
- ->assertSet('colors', ['#FF0000', '#00FF00'])
- ->assertSet('framework_class', 'Framework');
-});
-
-test('can update an existing device palette', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'original-palette',
- 'grays' => 2,
- 'framework_class' => '',
- 'source' => 'manual',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal', $palette->id)
- ->set('name', 'updated-palette')
- ->set('description', 'Updated Description')
- ->set('grays', 16)
- ->set('colors', ['#0000FF'])
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-
- $palette->refresh();
- expect($palette->name)->toBe('updated-palette');
- expect($palette->description)->toBe('Updated Description');
- expect($palette->grays)->toBe(16);
- expect($palette->colors)->toBe(['#0000FF']);
-});
-
-test('can delete a device palette', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'to-delete',
- 'grays' => 2,
- 'framework_class' => '',
- 'source' => 'manual',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('deleteDevicePalette', $palette->id);
-
- expect(DevicePalette::find($palette->id))->toBeNull();
- $component->assertSet('devicePalettes', function ($palettes) use ($palette) {
- return $palettes->where('id', $palette->id)->isEmpty();
- });
-});
-
-test('can duplicate a device palette', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'original-palette',
- 'description' => 'Original Description',
- 'grays' => 4,
- 'colors' => ['#FF0000', '#00FF00'],
- 'framework_class' => 'Framework',
- 'source' => 'manual',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('duplicateDevicePalette', $palette->id);
-
- $component
- ->assertSet('editingDevicePaletteId', null)
- ->assertSet('name', 'original-palette (Copy)')
- ->assertSet('description', 'Original Description')
- ->assertSet('grays', 4)
- ->assertSet('colors', ['#FF0000', '#00FF00'])
- ->assertSet('framework_class', 'Framework');
-});
-
-test('can add a color to the colors array', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colorInput', '#FF0000')
- ->call('addColor');
-
- $component
- ->assertHasNoErrors()
- ->assertSet('colors', ['#FF0000'])
- ->assertSet('colorInput', '');
-});
-
-test('cannot add duplicate colors', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colors', ['#FF0000'])
- ->set('colorInput', '#FF0000')
- ->call('addColor');
-
- $component
- ->assertHasNoErrors()
- ->assertSet('colors', ['#FF0000']);
-});
-
-test('can add multiple colors', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colorInput', '#FF0000')
- ->call('addColor')
- ->set('colorInput', '#00FF00')
- ->call('addColor')
- ->set('colorInput', '#0000FF')
- ->call('addColor');
-
- $component
- ->assertSet('colors', ['#FF0000', '#00FF00', '#0000FF']);
-});
-
-test('can remove a color from the colors array', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colors', ['#FF0000', '#00FF00', '#0000FF'])
- ->call('removeColor', 1);
-
- $component->assertSet('colors', ['#FF0000', '#0000FF']);
-});
-
-test('removing color reindexes array', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colors', ['#FF0000', '#00FF00', '#0000FF'])
- ->call('removeColor', 0);
-
- $colors = $component->get('colors');
- expect($colors)->toBe(['#00FF00', '#0000FF']);
- expect(array_keys($colors))->toBe([0, 1]);
-});
-
-test('can open modal in view-only mode for api-sourced palette', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'api-palette',
- 'grays' => 2,
- 'framework_class' => '',
- 'source' => 'api',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal', $palette->id, true);
-
- $component
- ->assertSet('viewingDevicePaletteId', $palette->id)
- ->assertSet('editingDevicePaletteId', null);
-});
-
-test('name is required when creating device palette', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('grays', 16)
- ->call('saveDevicePalette');
-
- $component->assertHasErrors(['name']);
-});
-
-test('name must be unique when creating device palette', function (): void {
- $user = User::factory()->create();
- DevicePalette::create([
- 'name' => 'existing-name',
- 'grays' => 2,
- 'framework_class' => '',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'existing-name')
- ->set('grays', 16)
- ->call('saveDevicePalette');
-
- $component->assertHasErrors(['name']);
-});
-
-test('name can be same when updating device palette', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'original-name',
- 'grays' => 2,
- 'framework_class' => '',
- 'source' => 'manual',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal', $palette->id)
- ->set('grays', 16)
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-});
-
-test('grays is required when creating device palette', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', null)
- ->call('saveDevicePalette');
-
- $component->assertHasErrors(['grays']);
-});
-
-test('grays must be at least 1', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', 0)
- ->call('saveDevicePalette');
-
- $component->assertHasErrors(['grays']);
-});
-
-test('grays must be at most 256', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', 257)
- ->call('saveDevicePalette');
-
- $component->assertHasErrors(['grays']);
-});
-
-test('colors must be valid hex format', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', 16)
- ->set('colors', ['invalid-color', '#FF0000'])
- ->call('saveDevicePalette');
-
- $component->assertHasErrors(['colors.0']);
-});
-
-test('color input must be valid hex format when adding color', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colorInput', 'invalid-color')
- ->call('addColor');
-
- $component->assertHasErrors(['colorInput']);
-});
-
-test('color input accepts valid hex format', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colorInput', '#FF0000')
- ->call('addColor');
-
- $component->assertHasNoErrors();
-});
-
-test('color input accepts lowercase hex format', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('colorInput', '#ff0000')
- ->call('addColor');
-
- $component->assertHasNoErrors();
-});
-
-test('description can be null', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', 16)
- ->set('description', null)
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-
- $palette = DevicePalette::where('name', 'test-palette')->first();
- expect($palette->description)->toBeNull();
-});
-
-test('framework class can be empty string', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', 16)
- ->set('framework_class', '')
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-
- $palette = DevicePalette::where('name', 'test-palette')->first();
- expect($palette->framework_class)->toBe('');
-});
-
-test('empty colors array is saved as null', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('grays', 16)
- ->set('colors', [])
- ->call('saveDevicePalette');
-
- $component->assertHasNoErrors();
-
- $palette = DevicePalette::where('name', 'test-palette')->first();
- expect($palette->colors)->toBeNull();
-});
-
-test('component resets form after saving', function (): void {
- $user = User::factory()->create();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'test-palette')
- ->set('description', 'Test Description')
- ->set('grays', 16)
- ->set('colors', ['#FF0000'])
- ->set('framework_class', 'TestFramework')
- ->call('saveDevicePalette');
-
- $component
- ->assertSet('name', null)
- ->assertSet('description', null)
- ->assertSet('grays', 2)
- ->assertSet('colors', [])
- ->assertSet('framework_class', '')
- ->assertSet('colorInput', '')
- ->assertSet('editingDevicePaletteId', null)
- ->assertSet('viewingDevicePaletteId', null);
-});
-
-test('component handles palette with null colors when editing', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'grayscale-palette',
- 'grays' => 2,
- 'colors' => null,
- 'framework_class' => '',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal', $palette->id);
-
- $component->assertSet('colors', []);
-});
-
-test('component handles palette with string colors when editing', function (): void {
- $user = User::factory()->create();
- $palette = DevicePalette::create([
- 'name' => 'string-colors-palette',
- 'grays' => 2,
- 'framework_class' => '',
- ]);
- // Manually set colors as JSON string to simulate edge case
- $palette->setRawAttributes(array_merge($palette->getAttributes(), [
- 'colors' => json_encode(['#FF0000', '#00FF00']),
- ]));
- $palette->save();
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('openDevicePaletteModal', $palette->id);
-
- $component->assertSet('colors', ['#FF0000', '#00FF00']);
-});
-
-test('component refreshes palette list after creating', function (): void {
- $user = User::factory()->create();
- $initialCount = DevicePalette::count();
- DevicePalette::create(['name' => 'palette-1', 'grays' => 2, 'framework_class' => '']);
- DevicePalette::create(['name' => 'palette-2', 'grays' => 4, 'framework_class' => '']);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->set('name', 'new-palette')
- ->set('grays', 16)
- ->call('saveDevicePalette');
-
- $palettes = $component->get('devicePalettes');
- expect($palettes)->toHaveCount($initialCount + 3);
- expect(DevicePalette::count())->toBe($initialCount + 3);
-});
-
-test('component refreshes palette list after deleting', function (): void {
- $user = User::factory()->create();
- $initialCount = DevicePalette::count();
- $palette1 = DevicePalette::create([
- 'name' => 'palette-1',
- 'grays' => 2,
- 'framework_class' => '',
- 'source' => 'manual',
- ]);
- $palette2 = DevicePalette::create([
- 'name' => 'palette-2',
- 'grays' => 2,
- 'framework_class' => '',
- 'source' => 'manual',
- ]);
-
- $this->actingAs($user);
-
- $component = Volt::test('device-palettes.index')
- ->call('deleteDevicePalette', $palette1->id);
-
- $palettes = $component->get('devicePalettes');
- expect($palettes)->toHaveCount($initialCount + 1);
- expect(DevicePalette::count())->toBe($initialCount + 1);
-});
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
index 963bc0c..44a4f33 100644
--- a/tests/Unit/ExampleTest.php
+++ b/tests/Unit/ExampleTest.php
@@ -1,5 +1,5 @@
toBeTrue();
});
diff --git a/tests/Unit/Liquid/Filters/DataTest.php b/tests/Unit/Liquid/Filters/DataTest.php
index 1200b6f..bdf649f 100644
--- a/tests/Unit/Liquid/Filters/DataTest.php
+++ b/tests/Unit/Liquid/Filters/DataTest.php
@@ -2,14 +2,14 @@
use App\Liquid\Filters\Data;
-test('json filter converts arrays to JSON', function (): void {
+test('json filter converts arrays to JSON', function () {
$filter = new Data();
$array = ['foo' => 'bar', 'baz' => 'qux'];
expect($filter->json($array))->toBe('{"foo":"bar","baz":"qux"}');
});
-test('json filter converts objects to JSON', function (): void {
+test('json filter converts objects to JSON', function () {
$filter = new Data();
$object = new stdClass();
$object->foo = 'bar';
@@ -18,7 +18,7 @@ test('json filter converts objects to JSON', function (): void {
expect($filter->json($object))->toBe('{"foo":"bar","baz":"qux"}');
});
-test('json filter handles nested structures', function (): void {
+test('json filter handles nested structures', function () {
$filter = new Data();
$nested = [
'foo' => 'bar',
@@ -31,7 +31,7 @@ test('json filter handles nested structures', function (): void {
expect($filter->json($nested))->toBe('{"foo":"bar","nested":{"baz":"qux","items":[1,2,3]}}');
});
-test('json filter handles scalar values', function (): void {
+test('json filter handles scalar values', function () {
$filter = new Data();
expect($filter->json('string'))->toBe('"string"');
@@ -40,21 +40,21 @@ test('json filter handles scalar values', function (): void {
expect($filter->json(null))->toBe('null');
});
-test('json filter preserves unicode characters', function (): void {
+test('json filter preserves unicode characters', function () {
$filter = new Data();
$data = ['message' => 'Hello, 世界'];
expect($filter->json($data))->toBe('{"message":"Hello, 世界"}');
});
-test('json filter does not escape slashes', function (): void {
+test('json filter does not escape slashes', function () {
$filter = new Data();
$data = ['url' => 'https://example.com/path'];
expect($filter->json($data))->toBe('{"url":"https://example.com/path"}');
});
-test('find_by filter finds object by key-value pair', function (): void {
+test('find_by filter finds object by key-value pair', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'age' => 35],
@@ -66,7 +66,7 @@ test('find_by filter finds object by key-value pair', function (): void {
expect($result)->toBe(['name' => 'Ryan', 'age' => 35]);
});
-test('find_by filter returns null when no match found', function (): void {
+test('find_by filter returns null when no match found', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'age' => 35],
@@ -78,7 +78,7 @@ test('find_by filter returns null when no match found', function (): void {
expect($result)->toBeNull();
});
-test('find_by filter returns fallback when no match found', function (): void {
+test('find_by filter returns fallback when no match found', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'age' => 35],
@@ -90,7 +90,7 @@ test('find_by filter returns fallback when no match found', function (): void {
expect($result)->toBe('Not Found');
});
-test('find_by filter finds by age', function (): void {
+test('find_by filter finds by age', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'age' => 35],
@@ -102,7 +102,7 @@ test('find_by filter finds by age', function (): void {
expect($result)->toBe(['name' => 'Sara', 'age' => 29]);
});
-test('find_by filter handles empty collection', function (): void {
+test('find_by filter handles empty collection', function () {
$filter = new Data();
$collection = [];
@@ -110,7 +110,7 @@ test('find_by filter handles empty collection', function (): void {
expect($result)->toBeNull();
});
-test('find_by filter handles collection with non-array items', function (): void {
+test('find_by filter handles collection with non-array items', function () {
$filter = new Data();
$collection = [
'not an array',
@@ -122,7 +122,7 @@ test('find_by filter handles collection with non-array items', function (): void
expect($result)->toBe(['name' => 'Ryan', 'age' => 35]);
});
-test('find_by filter handles items without the specified key', function (): void {
+test('find_by filter handles items without the specified key', function () {
$filter = new Data();
$collection = [
['age' => 35],
@@ -134,7 +134,7 @@ test('find_by filter handles items without the specified key', function (): void
expect($result)->toBe(['name' => 'Ryan', 'age' => 35]);
});
-test('group_by filter groups collection by age', function (): void {
+test('group_by filter groups collection by age', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'age' => 35],
@@ -153,7 +153,7 @@ test('group_by filter groups collection by age', function (): void {
]);
});
-test('group_by filter groups collection by name', function (): void {
+test('group_by filter groups collection by name', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'age' => 35],
@@ -172,7 +172,7 @@ test('group_by filter groups collection by name', function (): void {
]);
});
-test('group_by filter handles empty collection', function (): void {
+test('group_by filter handles empty collection', function () {
$filter = new Data();
$collection = [];
@@ -180,7 +180,7 @@ test('group_by filter handles empty collection', function (): void {
expect($result)->toBe([]);
});
-test('group_by filter handles collection with non-array items', function (): void {
+test('group_by filter handles collection with non-array items', function () {
$filter = new Data();
$collection = [
'not an array',
@@ -197,7 +197,7 @@ test('group_by filter handles collection with non-array items', function (): voi
]);
});
-test('group_by filter handles items without the specified key', function (): void {
+test('group_by filter handles items without the specified key', function () {
$filter = new Data();
$collection = [
['age' => 35],
@@ -217,7 +217,7 @@ test('group_by filter handles items without the specified key', function (): voi
]);
});
-test('group_by filter handles mixed data types as keys', function (): void {
+test('group_by filter handles mixed data types as keys', function () {
$filter = new Data();
$collection = [
['name' => 'Ryan', 'active' => true],
@@ -238,7 +238,7 @@ test('group_by filter handles mixed data types as keys', function (): void {
]);
});
-test('sample filter returns a random element from array', function (): void {
+test('sample filter returns a random element from array', function () {
$filter = new Data();
$array = ['1', '2', '3', '4', '5'];
@@ -246,7 +246,7 @@ test('sample filter returns a random element from array', function (): void {
expect($result)->toBeIn($array);
});
-test('sample filter returns a random element from string array', function (): void {
+test('sample filter returns a random element from string array', function () {
$filter = new Data();
$array = ['cat', 'dog'];
@@ -254,7 +254,7 @@ test('sample filter returns a random element from string array', function (): vo
expect($result)->toBeIn($array);
});
-test('sample filter returns null for empty array', function (): void {
+test('sample filter returns null for empty array', function () {
$filter = new Data();
$array = [];
@@ -262,7 +262,7 @@ test('sample filter returns null for empty array', function (): void {
expect($result)->toBeNull();
});
-test('sample filter returns the only element from single element array', function (): void {
+test('sample filter returns the only element from single element array', function () {
$filter = new Data();
$array = ['single'];
@@ -270,7 +270,7 @@ test('sample filter returns the only element from single element array', functio
expect($result)->toBe('single');
});
-test('sample filter works with mixed data types', function (): void {
+test('sample filter works with mixed data types', function () {
$filter = new Data();
$array = [1, 'string', true, null, ['nested']];
@@ -278,7 +278,7 @@ test('sample filter works with mixed data types', function (): void {
expect($result)->toBeIn($array);
});
-test('parse_json filter parses JSON string to array', function (): void {
+test('parse_json filter parses JSON string to array', function () {
$filter = new Data();
$jsonString = '[{"a":1,"b":"c"},"d"]';
@@ -286,7 +286,7 @@ test('parse_json filter parses JSON string to array', function (): void {
expect($result)->toBe([['a' => 1, 'b' => 'c'], 'd']);
});
-test('parse_json filter parses simple JSON object', function (): void {
+test('parse_json filter parses simple JSON object', function () {
$filter = new Data();
$jsonString = '{"name":"John","age":30,"city":"New York"}';
@@ -294,7 +294,7 @@ test('parse_json filter parses simple JSON object', function (): void {
expect($result)->toBe(['name' => 'John', 'age' => 30, 'city' => 'New York']);
});
-test('parse_json filter parses JSON array', function (): void {
+test('parse_json filter parses JSON array', function () {
$filter = new Data();
$jsonString = '["apple","banana","cherry"]';
@@ -302,7 +302,7 @@ test('parse_json filter parses JSON array', function (): void {
expect($result)->toBe(['apple', 'banana', 'cherry']);
});
-test('parse_json filter parses nested JSON structure', function (): void {
+test('parse_json filter parses nested JSON structure', function () {
$filter = new Data();
$jsonString = '{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}],"total":2}';
@@ -316,7 +316,7 @@ test('parse_json filter parses nested JSON structure', function (): void {
]);
});
-test('parse_json filter handles primitive values', function (): void {
+test('parse_json filter handles primitive values', function () {
$filter = new Data();
expect($filter->parse_json('"hello"'))->toBe('hello');
@@ -325,173 +325,3 @@ test('parse_json filter handles primitive values', function (): void {
expect($filter->parse_json('false'))->toBe(false);
expect($filter->parse_json('null'))->toBe(null);
});
-
-test('map_to_i filter converts string numbers to integers', function (): void {
- $filter = new Data();
- $input = ['1', '2', '3', '4', '5'];
-
- expect($filter->map_to_i($input))->toBe([1, 2, 3, 4, 5]);
-});
-
-test('map_to_i filter handles mixed string numbers', function (): void {
- $filter = new Data();
- $input = ['5', '4', '3', '2', '1'];
-
- expect($filter->map_to_i($input))->toBe([5, 4, 3, 2, 1]);
-});
-
-test('map_to_i filter handles decimal strings', function (): void {
- $filter = new Data();
- $input = ['1.5', '2.7', '3.0'];
-
- expect($filter->map_to_i($input))->toBe([1, 2, 3]);
-});
-
-test('map_to_i filter handles empty array', function (): void {
- $filter = new Data();
- $input = [];
-
- expect($filter->map_to_i($input))->toBe([]);
-});
-
-test('where_exp filter returns string as array when input is string', function (): void {
- $filter = new Data();
- $input = 'just a string';
-
- expect($filter->where_exp($input, 'la', 'le'))->toBe(['just a string']);
-});
-
-test('where_exp filter filters numbers with comparison', function (): void {
- $filter = new Data();
- $input = [1, 2, 3, 4, 5];
-
- expect($filter->where_exp($input, 'n', 'n >= 3'))->toBe([3, 4, 5]);
-});
-
-test('where_exp filter filters numbers with greater than', function (): void {
- $filter = new Data();
- $input = [1, 2, 3, 4, 5];
-
- expect($filter->where_exp($input, 'n', 'n > 2'))->toBe([3, 4, 5]);
-});
-
-test('where_exp filter filters numbers with less than', function (): void {
- $filter = new Data();
- $input = [1, 2, 3, 4, 5];
-
- expect($filter->where_exp($input, 'n', 'n < 4'))->toBe([1, 2, 3]);
-});
-
-test('where_exp filter filters numbers with equality', function (): void {
- $filter = new Data();
- $input = [1, 2, 3, 4, 5];
-
- expect($filter->where_exp($input, 'n', 'n == 3'))->toBe([3]);
-});
-
-test('where_exp filter filters numbers with not equal', function (): void {
- $filter = new Data();
- $input = [1, 2, 3, 4, 5];
-
- expect($filter->where_exp($input, 'n', 'n != 3'))->toBe([1, 2, 4, 5]);
-});
-
-test('where_exp filter filters objects by property', function (): void {
- $filter = new Data();
- $input = [
- ['name' => 'Alice', 'age' => 25],
- ['name' => 'Bob', 'age' => 30],
- ['name' => 'Charlie', 'age' => 35],
- ];
-
- expect($filter->where_exp($input, 'person', 'person.age >= 30'))->toBe([
- ['name' => 'Bob', 'age' => 30],
- ['name' => 'Charlie', 'age' => 35],
- ]);
-});
-
-test('where_exp filter filters objects by string property', function (): void {
- $filter = new Data();
- $input = [
- ['name' => 'Alice', 'role' => 'admin'],
- ['name' => 'Bob', 'role' => 'user'],
- ['name' => 'Charlie', 'role' => 'admin'],
- ];
-
- expect($filter->where_exp($input, 'user', 'user.role == "admin"'))->toBe([
- ['name' => 'Alice', 'role' => 'admin'],
- ['name' => 'Charlie', 'role' => 'admin'],
- ]);
-});
-
-test('where_exp filter handles and operator', function (): void {
- $filter = new Data();
- $input = [
- ['name' => 'Alice', 'age' => 25, 'active' => true],
- ['name' => 'Bob', 'age' => 30, 'active' => false],
- ['name' => 'Charlie', 'age' => 35, 'active' => true],
- ];
-
- expect($filter->where_exp($input, 'person', 'person.age >= 30 and person.active == true'))->toBe([
- ['name' => 'Charlie', 'age' => 35, 'active' => true],
- ]);
-});
-
-test('where_exp filter handles or operator', function (): void {
- $filter = new Data();
- $input = [
- ['name' => 'Alice', 'age' => 25, 'role' => 'admin'],
- ['name' => 'Bob', 'age' => 30, 'role' => 'user'],
- ['name' => 'Charlie', 'age' => 35, 'role' => 'user'],
- ];
-
- expect($filter->where_exp($input, 'person', 'person.age < 30 or person.role == "admin"'))->toBe([
- ['name' => 'Alice', 'age' => 25, 'role' => 'admin'],
- ]);
-});
-
-test('where_exp filter handles simple boolean expressions', function (): void {
- $filter = new Data();
- $input = [
- ['name' => 'Alice', 'active' => true],
- ['name' => 'Bob', 'active' => false],
- ['name' => 'Charlie', 'active' => true],
- ];
-
- expect($filter->where_exp($input, 'person', 'person.active'))->toBe([
- ['name' => 'Alice', 'active' => true],
- ['name' => 'Charlie', 'active' => true],
- ]);
-});
-
-test('where_exp filter handles empty array', function (): void {
- $filter = new Data();
- $input = [];
-
- expect($filter->where_exp($input, 'n', 'n >= 3'))->toBe([]);
-});
-
-test('where_exp filter handles associative array', function (): void {
- $filter = new Data();
- $input = [
- 'a' => 1,
- 'b' => 2,
- 'c' => 3,
- ];
-
- expect($filter->where_exp($input, 'n', 'n >= 2'))->toBe([2, 3]);
-});
-
-test('where_exp filter handles non-array input', function (): void {
- $filter = new Data();
- $input = 123;
-
- expect($filter->where_exp($input, 'n', 'n >= 3'))->toBe([]);
-});
-
-test('where_exp filter handles null input', function (): void {
- $filter = new Data();
- $input = null;
-
- expect($filter->where_exp($input, 'n', 'n >= 3'))->toBe([]);
-});
diff --git a/tests/Unit/Liquid/Filters/DateTest.php b/tests/Unit/Liquid/Filters/DateTest.php
index 7de8949..5813e10 100644
--- a/tests/Unit/Liquid/Filters/DateTest.php
+++ b/tests/Unit/Liquid/Filters/DateTest.php
@@ -3,92 +3,30 @@
use App\Liquid\Filters\Date;
use Carbon\Carbon;
-test('days_ago filter returns correct date', function (): void {
+test('days_ago filter returns correct date', function () {
$filter = new Date();
$threeDaysAgo = Carbon::now()->subDays(3)->toDateString();
expect($filter->days_ago(3))->toBe($threeDaysAgo);
});
-test('days_ago filter handles string input', function (): void {
+test('days_ago filter handles string input', function () {
$filter = new Date();
$fiveDaysAgo = Carbon::now()->subDays(5)->toDateString();
expect($filter->days_ago('5'))->toBe($fiveDaysAgo);
});
-test('days_ago filter with zero days returns today', function (): void {
+test('days_ago filter with zero days returns today', function () {
$filter = new Date();
$today = Carbon::now()->toDateString();
expect($filter->days_ago(0))->toBe($today);
});
-test('days_ago filter with large number works correctly', function (): void {
+test('days_ago filter with large number works correctly', function () {
$filter = new Date();
$hundredDaysAgo = Carbon::now()->subDays(100)->toDateString();
expect($filter->days_ago(100))->toBe($hundredDaysAgo);
});
-
-test('ordinalize filter formats date with ordinal day', function (): void {
- $filter = new Date();
-
- expect($filter->ordinalize('2025-10-02', '%A, %B <>, %Y'))
- ->toBe('Thursday, October 2nd, 2025');
-});
-
-test('ordinalize filter handles datetime string with timezone', function (): void {
- $filter = new Date();
-
- expect($filter->ordinalize('2025-12-31 16:50:38 -0400', '%A, %b <>'))
- ->toBe('Wednesday, Dec 31st');
-});
-
-test('ordinalize filter handles different ordinal suffixes', function (): void {
- $filter = new Date();
-
- // 1st
- expect($filter->ordinalize('2025-01-01', '<>'))
- ->toBe('1st');
-
- // 2nd
- expect($filter->ordinalize('2025-01-02', '<>'))
- ->toBe('2nd');
-
- // 3rd
- expect($filter->ordinalize('2025-01-03', '<>'))
- ->toBe('3rd');
-
- // 4th
- expect($filter->ordinalize('2025-01-04', '<>'))
- ->toBe('4th');
-
- // 11th (special case)
- expect($filter->ordinalize('2025-01-11', '<>'))
- ->toBe('11th');
-
- // 12th (special case)
- expect($filter->ordinalize('2025-01-12', '<>'))
- ->toBe('12th');
-
- // 13th (special case)
- expect($filter->ordinalize('2025-01-13', '<>'))
- ->toBe('13th');
-
- // 21st
- expect($filter->ordinalize('2025-01-21', '<>'))
- ->toBe('21st');
-
- // 22nd
- expect($filter->ordinalize('2025-01-22', '<>'))
- ->toBe('22nd');
-
- // 23rd
- expect($filter->ordinalize('2025-01-23', '<>'))
- ->toBe('23rd');
-
- // 24th
- expect($filter->ordinalize('2025-01-24', '<>'))
- ->toBe('24th');
-});
diff --git a/tests/Unit/Liquid/Filters/LocalizationTest.php b/tests/Unit/Liquid/Filters/LocalizationTest.php
index 3129b1e..384c837 100644
--- a/tests/Unit/Liquid/Filters/LocalizationTest.php
+++ b/tests/Unit/Liquid/Filters/LocalizationTest.php
@@ -2,7 +2,7 @@
use App\Liquid\Filters\Localization;
-test('l_date formats date with default format', function (): void {
+test('l_date formats date with default format', function () {
$filter = new Localization();
$date = '2025-01-11';
@@ -15,7 +15,7 @@ test('l_date formats date with default format', function (): void {
expect($result)->toContain('11');
});
-test('l_date formats date with custom format', function (): void {
+test('l_date formats date with custom format', function () {
$filter = new Localization();
$date = '2025-01-11';
@@ -27,7 +27,7 @@ test('l_date formats date with custom format', function (): void {
// We can't check for 'Jan' specifically as it might be localized
});
-test('l_date handles DateTime objects', function (): void {
+test('l_date handles DateTime objects', function () {
$filter = new Localization();
$date = new DateTimeImmutable('2025-01-11');
@@ -36,102 +36,27 @@ test('l_date handles DateTime objects', function (): void {
expect($result)->toContain('2025-01-11');
});
-test('l_word translates common words', function (): void {
+test('l_word translates common words', function () {
$filter = new Localization();
expect($filter->l_word('today', 'de'))->toBe('heute');
});
-test('l_word returns original word if no translation exists', function (): void {
+test('l_word returns original word if no translation exists', function () {
$filter = new Localization();
expect($filter->l_word('hello', 'es-ES'))->toBe('hello');
expect($filter->l_word('world', 'ko'))->toBe('world');
});
-test('l_word is case-insensitive', function (): void {
+test('l_word is case-insensitive', function () {
$filter = new Localization();
expect($filter->l_word('TODAY', 'de'))->toBe('heute');
});
-test('l_word returns original word for unknown locales', function (): void {
+test('l_word returns original word for unknown locales', function () {
$filter = new Localization();
expect($filter->l_word('today', 'unknown-locale'))->toBe('today');
});
-
-test('l_date handles locale parameter', function (): void {
- $filter = new Localization();
- $date = '2025-01-11';
-
- $result = $filter->l_date($date, 'Y-m-d', 'de');
-
- // The result should still contain the date components
- expect($result)->toContain('2025');
- expect($result)->toContain('01');
- expect($result)->toContain('11');
-});
-
-test('l_date handles null locale parameter', function (): void {
- $filter = new Localization();
- $date = '2025-01-11';
-
- $result = $filter->l_date($date, 'Y-m-d');
-
- // Should work the same as default
- expect($result)->toContain('2025');
- expect($result)->toContain('01');
- expect($result)->toContain('11');
-});
-
-test('l_date handles different date formats with locale', function (): void {
- $filter = new Localization();
- $date = '2025-01-11';
-
- $result = $filter->l_date($date, '%B %d, %Y', 'en');
-
- // Should contain the month name and date
- expect($result)->toContain('2025');
- expect($result)->toContain('11');
-});
-
-test('l_date handles DateTimeInterface objects with locale', function (): void {
- $filter = new Localization();
- $date = new DateTimeImmutable('2025-01-11');
-
- $result = $filter->l_date($date, 'Y-m-d', 'fr');
-
- // Should still format correctly
- expect($result)->toContain('2025');
- expect($result)->toContain('01');
- expect($result)->toContain('11');
-});
-
-test('l_date handles invalid date gracefully', function (): void {
- $filter = new Localization();
- $invalidDate = 'invalid-date';
-
- // This should throw an exception or return a default value
- // The exact behavior depends on Carbon's implementation
- expect(fn (): string => $filter->l_date($invalidDate))->toThrow(Exception::class);
-});
-
-test('l_word handles empty string', function (): void {
- $filter = new Localization();
-
- expect($filter->l_word('', 'de'))->toBe('');
-});
-
-test('l_word handles special characters', function (): void {
- $filter = new Localization();
-
- // Test with a word that has special characters
- expect($filter->l_word('café', 'de'))->toBe('café');
-});
-
-test('l_word handles numeric strings', function (): void {
- $filter = new Localization();
-
- expect($filter->l_word('123', 'de'))->toBe('123');
-});
diff --git a/tests/Unit/Liquid/Filters/NumbersTest.php b/tests/Unit/Liquid/Filters/NumbersTest.php
index 42deffb..8ea73bf 100644
--- a/tests/Unit/Liquid/Filters/NumbersTest.php
+++ b/tests/Unit/Liquid/Filters/NumbersTest.php
@@ -2,7 +2,7 @@
use App\Liquid\Filters\Numbers;
-test('number_with_delimiter formats numbers with commas by default', function (): void {
+test('number_with_delimiter formats numbers with commas by default', function () {
$filter = new Numbers();
expect($filter->number_with_delimiter(1234))->toBe('1,234');
@@ -10,21 +10,21 @@ test('number_with_delimiter formats numbers with commas by default', function ()
expect($filter->number_with_delimiter(0))->toBe('0');
});
-test('number_with_delimiter handles custom delimiters', function (): void {
+test('number_with_delimiter handles custom delimiters', function () {
$filter = new Numbers();
expect($filter->number_with_delimiter(1234, '.'))->toBe('1.234');
expect($filter->number_with_delimiter(1000000, ' '))->toBe('1 000 000');
});
-test('number_with_delimiter handles decimal values with custom separators', function (): void {
+test('number_with_delimiter handles decimal values with custom separators', function () {
$filter = new Numbers();
expect($filter->number_with_delimiter(1234.57, ' ', ','))->toBe('1 234,57');
expect($filter->number_with_delimiter(1234.5, '.', ','))->toBe('1.234,50');
});
-test('number_to_currency formats numbers with dollar sign by default', function (): void {
+test('number_to_currency formats numbers with dollar sign by default', function () {
$filter = new Numbers();
expect($filter->number_to_currency(1234))->toBe('$1,234');
@@ -32,107 +32,16 @@ test('number_to_currency formats numbers with dollar sign by default', function
expect($filter->number_to_currency(0))->toBe('$0');
});
-test('number_to_currency handles custom currency symbols', function (): void {
+test('number_to_currency handles custom currency symbols', function () {
$filter = new Numbers();
expect($filter->number_to_currency(1234, '£'))->toBe('£1,234');
expect($filter->number_to_currency(152350.69, '€'))->toBe('€152,350.69');
});
-test('number_to_currency handles custom delimiters and separators', function (): void {
+test('number_to_currency handles custom delimiters and separators', function () {
$filter = new Numbers();
- $result1 = $filter->number_to_currency(1234.57, '£', '.', ',');
- $result2 = $filter->number_to_currency(1234.57, '€', ',', '.');
-
- expect($result1)->toContain('1.234,57');
- expect($result1)->toContain('£');
- expect($result2)->toContain('1,234.57');
- expect($result2)->toContain('€');
-});
-
-test('number_with_delimiter handles string numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_with_delimiter('1234'))->toBe('1,234');
- expect($filter->number_with_delimiter('1234.56'))->toBe('1,234.56');
-});
-
-test('number_with_delimiter handles negative numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_with_delimiter(-1234))->toBe('-1,234');
- expect($filter->number_with_delimiter(-1234.56))->toBe('-1,234.56');
-});
-
-test('number_with_delimiter handles zero', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_with_delimiter(0))->toBe('0');
- expect($filter->number_with_delimiter(0.0))->toBe('0.00');
-});
-
-test('number_with_delimiter handles very small numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_with_delimiter(0.01))->toBe('0.01');
- expect($filter->number_with_delimiter(0.001))->toBe('0.00');
-});
-
-test('number_to_currency handles string numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_to_currency('1234'))->toBe('$1,234');
- expect($filter->number_to_currency('1234.56'))->toBe('$1,234.56');
-});
-
-test('number_to_currency handles negative numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_to_currency(-1234))->toBe('-$1,234');
- expect($filter->number_to_currency(-1234.56))->toBe('-$1,234.56');
-});
-
-test('number_to_currency handles zero', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_to_currency(0))->toBe('$0');
- expect($filter->number_to_currency(0.0))->toBe('$0.00');
-});
-
-test('number_to_currency handles currency code conversion', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_to_currency(1234, '$'))->toBe('$1,234');
- expect($filter->number_to_currency(1234, '€'))->toBe('€1,234');
- expect($filter->number_to_currency(1234, '£'))->toBe('£1,234');
-});
-
-test('number_to_currency handles German locale formatting', function (): void {
- $filter = new Numbers();
-
- // When delimiter is '.' and separator is ',', it should use German locale
- $result = $filter->number_to_currency(1234.56, 'EUR', '.', ',');
- expect($result)->toContain('1.234,56');
-});
-
-test('number_with_delimiter handles different decimal separators', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_with_delimiter(1234.56, ',', ','))->toBe('1,234,56');
- expect($filter->number_with_delimiter(1234.56, ' ', ','))->toBe('1 234,56');
-});
-
-test('number_to_currency handles very large numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_to_currency(1000000))->toBe('$1,000,000');
- expect($filter->number_to_currency(1000000.50))->toBe('$1,000,000.50');
-});
-
-test('number_with_delimiter handles very large numbers', function (): void {
- $filter = new Numbers();
-
- expect($filter->number_with_delimiter(1000000))->toBe('1,000,000');
- expect($filter->number_with_delimiter(1000000.50))->toBe('1,000,000.50');
+ expect($filter->number_to_currency(1234.57, '£', '.', ','))->toBe('1.234,57 £');
+ expect($filter->number_to_currency(1234.57, '€', ',', '.'))->toBe('€1,234.57');
});
diff --git a/tests/Unit/Liquid/Filters/StringMarkupTest.php b/tests/Unit/Liquid/Filters/StringMarkupTest.php
index bfd1a07..4021a07 100644
--- a/tests/Unit/Liquid/Filters/StringMarkupTest.php
+++ b/tests/Unit/Liquid/Filters/StringMarkupTest.php
@@ -2,35 +2,35 @@
use App\Liquid\Filters\StringMarkup;
-test('pluralize returns singular form with count 1', function (): void {
+test('pluralize returns singular form with count 1', function () {
$filter = new StringMarkup();
expect($filter->pluralize('book', 1))->toBe('1 book');
expect($filter->pluralize('person', 1))->toBe('1 person');
});
-test('pluralize returns plural form with count greater than 1', function (): void {
+test('pluralize returns plural form with count greater than 1', function () {
$filter = new StringMarkup();
expect($filter->pluralize('book', 2))->toBe('2 books');
expect($filter->pluralize('person', 4))->toBe('4 people');
});
-test('pluralize handles irregular plurals correctly', function (): void {
+test('pluralize handles irregular plurals correctly', function () {
$filter = new StringMarkup();
expect($filter->pluralize('child', 3))->toBe('3 children');
expect($filter->pluralize('sheep', 5))->toBe('5 sheep');
});
-test('pluralize uses default count of 2 when not specified', function (): void {
+test('pluralize uses default count of 2 when not specified', function () {
$filter = new StringMarkup();
expect($filter->pluralize('book'))->toBe('2 books');
expect($filter->pluralize('person'))->toBe('2 people');
});
-test('markdown_to_html converts basic markdown to HTML', function (): void {
+test('markdown_to_html converts basic markdown to HTML', function () {
$filter = new StringMarkup();
$markdown = 'This is *italic* and **bold**.';
@@ -42,7 +42,7 @@ test('markdown_to_html converts basic markdown to HTML', function (): void {
expect($result)->toContain('bold');
});
-test('markdown_to_html converts links correctly', function (): void {
+test('markdown_to_html converts links correctly', function () {
$filter = new StringMarkup();
$markdown = 'This is [a link](https://example.com).';
@@ -51,7 +51,7 @@ test('markdown_to_html converts links correctly', function (): void {
expect($result)->toContain('a link');
});
-test('markdown_to_html handles fallback when Parsedown is not available', function (): void {
+test('markdown_to_html handles fallback when Parsedown is not available', function () {
// Create a mock that simulates Parsedown not being available
$filter = new class extends StringMarkup
{
@@ -68,103 +68,23 @@ test('markdown_to_html handles fallback when Parsedown is not available', functi
expect($result)->toBe('This is *italic* and [a link](https://example.com).');
});
-test('strip_html removes HTML tags', function (): void {
+test('strip_html removes HTML tags', function () {
$filter = new StringMarkup();
$html = 'This is bold and italic.
';
expect($filter->strip_html($html))->toBe('This is bold and italic.');
});
-test('strip_html preserves text content', function (): void {
+test('strip_html preserves text content', function () {
$filter = new StringMarkup();
$html = 'Hello, world!
';
expect($filter->strip_html($html))->toBe('Hello, world!');
});
-test('strip_html handles nested tags', function (): void {
+test('strip_html handles nested tags', function () {
$filter = new StringMarkup();
$html = 'Paragraph with nested tags.
';
expect($filter->strip_html($html))->toBe('Paragraph with nested tags.');
});
-
-test('markdown_to_html handles CommonMarkException gracefully', function (): void {
- $filter = new StringMarkup();
-
- // Create a mock that throws CommonMarkException
- $filter = new class extends StringMarkup
- {
- public function markdown_to_html(string $markdown): ?string
- {
- try {
- // Simulate CommonMarkException
- throw new Exception('Invalid markdown');
- } catch (Exception $e) {
- Illuminate\Support\Facades\Log::error('Markdown conversion error: '.$e->getMessage());
- }
-
- return null;
- }
- };
-
- $result = $filter->markdown_to_html('invalid markdown');
-
- expect($result)->toBeNull();
-});
-
-test('markdown_to_html handles empty string', function (): void {
- $filter = new StringMarkup();
-
- $result = $filter->markdown_to_html('');
-
- expect($result)->toBe('');
-});
-
-test('markdown_to_html handles complex markdown', function (): void {
- $filter = new StringMarkup();
- $markdown = "# Heading\n\nThis is a paragraph with **bold** and *italic* text.\n\n- List item 1\n- List item 2\n\n[Link](https://example.com)";
-
- $result = $filter->markdown_to_html($markdown);
-
- expect($result)->toContain('Heading
');
- expect($result)->toContain('bold');
- expect($result)->toContain('italic');
- expect($result)->toContain('');
- expect($result)->toContain('- List item 1
');
- expect($result)->toContain('Link');
-});
-
-test('strip_html handles empty string', function (): void {
- $filter = new StringMarkup();
-
- expect($filter->strip_html(''))->toBe('');
-});
-
-test('strip_html handles string without HTML tags', function (): void {
- $filter = new StringMarkup();
- $text = 'This is plain text without any HTML tags.';
-
- expect($filter->strip_html($text))->toBe($text);
-});
-
-test('strip_html handles self-closing tags', function (): void {
- $filter = new StringMarkup();
- $html = 'Text with
line break and
horizontal rule.';
-
- expect($filter->strip_html($html))->toBe('Text with line break and horizontal rule.');
-});
-
-test('pluralize handles zero count', function (): void {
- $filter = new StringMarkup();
-
- expect($filter->pluralize('book', 0))->toBe('0 books');
- expect($filter->pluralize('person', 0))->toBe('0 people');
-});
-
-test('pluralize handles negative count', function (): void {
- $filter = new StringMarkup();
-
- expect($filter->pluralize('book', -1))->toBe('-1 book');
- expect($filter->pluralize('person', -5))->toBe('-5 people');
-});
diff --git a/tests/Unit/Liquid/Filters/UniquenessTest.php b/tests/Unit/Liquid/Filters/UniquenessTest.php
index 76840e1..291f312 100644
--- a/tests/Unit/Liquid/Filters/UniquenessTest.php
+++ b/tests/Unit/Liquid/Filters/UniquenessTest.php
@@ -2,7 +2,7 @@
use App\Liquid\Filters\Uniqueness;
-test('append_random appends a random string with 4 characters', function (): void {
+test('append_random appends a random string with 4 characters', function () {
$filter = new Uniqueness();
$result = $filter->append_random('chart-');
diff --git a/tests/Unit/Liquid/Utils/ExpressionUtilsTest.php b/tests/Unit/Liquid/Utils/ExpressionUtilsTest.php
deleted file mode 100644
index ee4d2fd..0000000
--- a/tests/Unit/Liquid/Utils/ExpressionUtilsTest.php
+++ /dev/null
@@ -1,201 +0,0 @@
- 1, 'b' => 2, 'c' => 3];
-
- expect(ExpressionUtils::isAssociativeArray($array))->toBeTrue();
-});
-
-test('isAssociativeArray returns false for indexed array', function (): void {
- $array = [1, 2, 3, 4, 5];
-
- expect(ExpressionUtils::isAssociativeArray($array))->toBeFalse();
-});
-
-test('isAssociativeArray returns false for empty array', function (): void {
- $array = [];
-
- expect(ExpressionUtils::isAssociativeArray($array))->toBeFalse();
-});
-
-test('parseCondition handles simple comparison', function (): void {
- $result = ExpressionUtils::parseCondition('n >= 3');
-
- expect($result)->toBe([
- 'type' => 'comparison',
- 'left' => 'n',
- 'operator' => '>=',
- 'right' => '3',
- ]);
-});
-
-test('parseCondition handles equality comparison', function (): void {
- $result = ExpressionUtils::parseCondition('user.role == "admin"');
-
- expect($result)->toBe([
- 'type' => 'comparison',
- 'left' => 'user.role',
- 'operator' => '==',
- 'right' => '"admin"',
- ]);
-});
-
-test('parseCondition handles and operator', function (): void {
- $result = ExpressionUtils::parseCondition('user.age >= 30 and user.active == true');
-
- expect($result)->toBe([
- 'type' => 'and',
- 'left' => [
- 'type' => 'comparison',
- 'left' => 'user.age',
- 'operator' => '>=',
- 'right' => '30',
- ],
- 'right' => [
- 'type' => 'comparison',
- 'left' => 'user.active',
- 'operator' => '==',
- 'right' => 'true',
- ],
- ]);
-});
-
-test('parseCondition handles or operator', function (): void {
- $result = ExpressionUtils::parseCondition('user.age < 30 or user.role == "admin"');
-
- expect($result)->toBe([
- 'type' => 'or',
- 'left' => [
- 'type' => 'comparison',
- 'left' => 'user.age',
- 'operator' => '<',
- 'right' => '30',
- ],
- 'right' => [
- 'type' => 'comparison',
- 'left' => 'user.role',
- 'operator' => '==',
- 'right' => '"admin"',
- ],
- ]);
-});
-
-test('parseCondition handles simple expression', function (): void {
- $result = ExpressionUtils::parseCondition('user.active');
-
- expect($result)->toBe([
- 'type' => 'simple',
- 'expression' => 'user.active',
- ]);
-});
-
-test('evaluateCondition handles comparison with numbers', function (): void {
- $condition = ExpressionUtils::parseCondition('n >= 3');
-
- expect(ExpressionUtils::evaluateCondition($condition, 'n', 5))->toBeTrue();
- expect(ExpressionUtils::evaluateCondition($condition, 'n', 2))->toBeFalse();
- expect(ExpressionUtils::evaluateCondition($condition, 'n', 3))->toBeTrue();
-});
-
-test('evaluateCondition handles comparison with strings', function (): void {
- $condition = ExpressionUtils::parseCondition('user.role == "admin"');
- $user = ['role' => 'admin'];
-
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeTrue();
-
- $user = ['role' => 'user'];
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeFalse();
-});
-
-test('evaluateCondition handles and operator', function (): void {
- $condition = ExpressionUtils::parseCondition('user.age >= 30 and user.active == true');
- $user = ['age' => 35, 'active' => true];
-
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeTrue();
-
- $user = ['age' => 25, 'active' => true];
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeFalse();
-
- $user = ['age' => 35, 'active' => false];
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeFalse();
-});
-
-test('evaluateCondition handles or operator', function (): void {
- $condition = ExpressionUtils::parseCondition('user.age < 30 or user.role == "admin"');
- $user = ['age' => 25, 'role' => 'user'];
-
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeTrue();
-
- $user = ['age' => 35, 'role' => 'admin'];
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeTrue();
-
- $user = ['age' => 35, 'role' => 'user'];
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeFalse();
-});
-
-test('evaluateCondition handles simple boolean expression', function (): void {
- $condition = ExpressionUtils::parseCondition('user.active');
- $user = ['active' => true];
-
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeTrue();
-
- $user = ['active' => false];
- expect(ExpressionUtils::evaluateCondition($condition, 'user', $user))->toBeFalse();
-});
-
-test('resolveValue returns object when expression matches variable', function (): void {
- $object = ['name' => 'Alice', 'age' => 25];
-
- expect(ExpressionUtils::resolveValue('user', 'user', $object))->toBe($object);
-});
-
-test('resolveValue resolves property access for arrays', function (): void {
- $object = ['name' => 'Alice', 'age' => 25];
-
- expect(ExpressionUtils::resolveValue('user.name', 'user', $object))->toBe('Alice');
- expect(ExpressionUtils::resolveValue('user.age', 'user', $object))->toBe(25);
-});
-
-test('resolveValue resolves property access for objects', function (): void {
- $object = new stdClass();
- $object->name = 'Alice';
- $object->age = 25;
-
- expect(ExpressionUtils::resolveValue('user.name', 'user', $object))->toBe('Alice');
- expect(ExpressionUtils::resolveValue('user.age', 'user', $object))->toBe(25);
-});
-
-test('resolveValue returns null for non-existent properties', function (): void {
- $object = ['name' => 'Alice'];
-
- expect(ExpressionUtils::resolveValue('user.age', 'user', $object))->toBeNull();
-});
-
-test('resolveValue parses numeric values', function (): void {
- expect(ExpressionUtils::resolveValue('123', 'user', []))->toBe(123);
- expect(ExpressionUtils::resolveValue('45.67', 'user', []))->toBe(45.67);
-});
-
-test('resolveValue parses boolean values', function (): void {
- expect(ExpressionUtils::resolveValue('true', 'user', []))->toBeTrue();
- expect(ExpressionUtils::resolveValue('false', 'user', []))->toBeFalse();
- expect(ExpressionUtils::resolveValue('TRUE', 'user', []))->toBeTrue();
- expect(ExpressionUtils::resolveValue('FALSE', 'user', []))->toBeFalse();
-});
-
-test('resolveValue parses null value', function (): void {
- expect(ExpressionUtils::resolveValue('null', 'user', []))->toBeNull();
- expect(ExpressionUtils::resolveValue('NULL', 'user', []))->toBeNull();
-});
-
-test('resolveValue removes quotes from strings', function (): void {
- expect(ExpressionUtils::resolveValue('"hello"', 'user', []))->toBe('hello');
- expect(ExpressionUtils::resolveValue("'world'", 'user', []))->toBe('world');
-});
-
-test('resolveValue returns expression as-is for unquoted strings', function (): void {
- expect(ExpressionUtils::resolveValue('hello', 'user', []))->toBe('hello');
- expect(ExpressionUtils::resolveValue('world', 'user', []))->toBe('world');
-});
diff --git a/tests/Unit/Models/DeviceLogTest.php b/tests/Unit/Models/DeviceLogTest.php
index f28f4cd..37e128f 100644
--- a/tests/Unit/Models/DeviceLogTest.php
+++ b/tests/Unit/Models/DeviceLogTest.php
@@ -6,7 +6,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
-test('device log belongs to a device', function (): void {
+test('device log belongs to a device', function () {
$device = Device::factory()->create();
$log = DeviceLog::factory()->create(['device_id' => $device->id]);
@@ -14,7 +14,7 @@ test('device log belongs to a device', function (): void {
->and($log->device->id)->toBe($device->id);
});
-test('device log casts log_entry to array', function (): void {
+test('device log casts log_entry to array', function () {
Device::factory()->create();
$log = DeviceLog::factory()->create([
'log_entry' => [
@@ -29,7 +29,7 @@ test('device log casts log_entry to array', function (): void {
->and($log->log_entry['level'])->toBe('info');
});
-test('device log casts device_timestamp to datetime', function (): void {
+test('device log casts device_timestamp to datetime', function () {
Device::factory()->create();
$timestamp = now();
$log = DeviceLog::factory()->create([
@@ -40,7 +40,7 @@ test('device log casts device_timestamp to datetime', function (): void {
->and($log->device_timestamp->timestamp)->toBe($timestamp->timestamp);
});
-test('device log factory creates valid data', function (): void {
+test('device log factory creates valid data', function () {
Device::factory()->create();
$log = DeviceLog::factory()->create();
@@ -50,7 +50,7 @@ test('device log factory creates valid data', function (): void {
->and($log->log_entry)->toHaveKeys(['creation_timestamp', 'device_status_stamp', 'log_id', 'log_message', 'log_codeline', 'log_sourcefile', 'additional_info']);
});
-test('device log can be created with minimal required fields', function (): void {
+test('device log can be created with minimal required fields', function () {
$device = Device::factory()->create();
$log = DeviceLog::create([
'device_id' => $device->id,
diff --git a/tests/Unit/Models/DeviceModelTest.php b/tests/Unit/Models/DeviceModelTest.php
deleted file mode 100644
index 8c2b6e9..0000000
--- a/tests/Unit/Models/DeviceModelTest.php
+++ /dev/null
@@ -1,119 +0,0 @@
-create([
- 'name' => 'Test Model',
- 'width' => 800,
- 'height' => 480,
- 'colors' => 4,
- 'bit_depth' => 2,
- 'scale_factor' => 1.0,
- 'rotation' => 0,
- 'offset_x' => 0,
- 'offset_y' => 0,
- ]);
-
- expect($deviceModel->name)->toBe('Test Model');
- expect($deviceModel->width)->toBe(800);
- expect($deviceModel->height)->toBe(480);
- expect($deviceModel->colors)->toBe(4);
- expect($deviceModel->bit_depth)->toBe(2);
- expect($deviceModel->scale_factor)->toBe(1.0);
- expect($deviceModel->rotation)->toBe(0);
- expect($deviceModel->offset_x)->toBe(0);
- expect($deviceModel->offset_y)->toBe(0);
-});
-
-test('device model casts attributes correctly', function (): void {
- $deviceModel = DeviceModel::factory()->create([
- 'width' => '800',
- 'height' => '480',
- 'colors' => '4',
- 'bit_depth' => '2',
- 'scale_factor' => '1.5',
- 'rotation' => '90',
- 'offset_x' => '10',
- 'offset_y' => '20',
- ]);
-
- expect($deviceModel->width)->toBeInt();
- expect($deviceModel->height)->toBeInt();
- expect($deviceModel->colors)->toBeInt();
- expect($deviceModel->bit_depth)->toBeInt();
- expect($deviceModel->scale_factor)->toBeFloat();
- expect($deviceModel->rotation)->toBeInt();
- expect($deviceModel->offset_x)->toBeInt();
- expect($deviceModel->offset_y)->toBeInt();
-});
-
-test('get color depth attribute returns correct format for bit depth 2', function (): void {
- $deviceModel = DeviceModel::factory()->create(['bit_depth' => 2]);
-
- expect($deviceModel->getColorDepthAttribute())->toBe('2bit');
-});
-
-test('get color depth attribute returns correct format for bit depth 4', function (): void {
- $deviceModel = DeviceModel::factory()->create(['bit_depth' => 4]);
-
- expect($deviceModel->getColorDepthAttribute())->toBe('4bit');
-});
-
-test('get color depth attribute returns 4bit for bit depth greater than 4', function (): void {
- $deviceModel = DeviceModel::factory()->create(['bit_depth' => 8]);
-
- expect($deviceModel->getColorDepthAttribute())->toBe('4bit');
-});
-
-test('get color depth attribute returns null when bit depth is null', function (): void {
- $deviceModel = new DeviceModel(['bit_depth' => null]);
-
- expect($deviceModel->getColorDepthAttribute())->toBeNull();
-});
-
-test('get scale level attribute returns null for width 800 or less', function (): void {
- $deviceModel = DeviceModel::factory()->create(['width' => 800]);
-
- expect($deviceModel->getScaleLevelAttribute())->toBeNull();
-});
-
-test('get scale level attribute returns large for width between 801 and 1000', function (): void {
- $deviceModel = DeviceModel::factory()->create(['width' => 900]);
-
- expect($deviceModel->getScaleLevelAttribute())->toBe('large');
-});
-
-test('get scale level attribute returns xlarge for width between 1001 and 1400', function (): void {
- $deviceModel = DeviceModel::factory()->create(['width' => 1200]);
-
- expect($deviceModel->getScaleLevelAttribute())->toBe('xlarge');
-});
-
-test('get scale level attribute returns xxlarge for width greater than 1400', function (): void {
- $deviceModel = DeviceModel::factory()->create(['width' => 1500]);
-
- expect($deviceModel->getScaleLevelAttribute())->toBe('xxlarge');
-});
-
-test('get scale level attribute returns null when width is null', function (): void {
- $deviceModel = new DeviceModel(['width' => null]);
-
- expect($deviceModel->getScaleLevelAttribute())->toBeNull();
-});
-
-test('device model factory creates valid data', function (): void {
- $deviceModel = DeviceModel::factory()->create();
-
- expect($deviceModel->name)->not->toBeEmpty();
- expect($deviceModel->width)->toBeInt();
- expect($deviceModel->height)->toBeInt();
- expect($deviceModel->colors)->toBeInt();
- expect($deviceModel->bit_depth)->toBeInt();
- expect($deviceModel->scale_factor)->toBeFloat();
- expect($deviceModel->rotation)->toBeInt();
- expect($deviceModel->offset_x)->toBeInt();
- expect($deviceModel->offset_y)->toBeInt();
-});
diff --git a/tests/Unit/Models/PlaylistItemTest.php b/tests/Unit/Models/PlaylistItemTest.php
index 428a165..6bfe00c 100644
--- a/tests/Unit/Models/PlaylistItemTest.php
+++ b/tests/Unit/Models/PlaylistItemTest.php
@@ -4,7 +4,7 @@ use App\Models\Playlist;
use App\Models\PlaylistItem;
use App\Models\Plugin;
-test('playlist item belongs to playlist', function (): void {
+test('playlist item belongs to playlist', function () {
$playlist = Playlist::factory()->create();
$playlistItem = PlaylistItem::factory()->create(['playlist_id' => $playlist->id]);
@@ -13,7 +13,7 @@ test('playlist item belongs to playlist', function (): void {
->id->toBe($playlist->id);
});
-test('playlist item belongs to plugin', function (): void {
+test('playlist item belongs to plugin', function () {
$plugin = Plugin::factory()->create();
$playlistItem = PlaylistItem::factory()->create(['plugin_id' => $plugin->id]);
@@ -22,7 +22,7 @@ test('playlist item belongs to plugin', function (): void {
->id->toBe($plugin->id);
});
-test('playlist item can check if it is a mashup', function (): void {
+test('playlist item can check if it is a mashup', function () {
$plugin = Plugin::factory()->create();
$regularItem = PlaylistItem::factory()->create([
'mashup' => null,
@@ -44,7 +44,7 @@ test('playlist item can check if it is a mashup', function (): void {
->and($mashupItem->isMashup())->toBeTrue();
});
-test('playlist item can get mashup name', function (): void {
+test('playlist item can get mashup name', function () {
$plugin1 = Plugin::factory()->create();
$plugin2 = Plugin::factory()->create();
$mashupItem = PlaylistItem::factory()->create([
@@ -59,7 +59,7 @@ test('playlist item can get mashup name', function (): void {
expect($mashupItem->getMashupName())->toBe('Test Mashup');
});
-test('playlist item can get mashup layout type', function (): void {
+test('playlist item can get mashup layout type', function () {
$plugin1 = Plugin::factory()->create();
$plugin2 = Plugin::factory()->create();
$mashupItem = PlaylistItem::factory()->create([
@@ -74,7 +74,7 @@ test('playlist item can get mashup layout type', function (): void {
expect($mashupItem->getMashupLayoutType())->toBe('1Lx1R');
});
-test('playlist item can get mashup plugin ids', function (): void {
+test('playlist item can get mashup plugin ids', function () {
$plugin1 = Plugin::factory()->create();
$plugin2 = Plugin::factory()->create();
$mashupItem = PlaylistItem::factory()->create([
@@ -89,7 +89,7 @@ test('playlist item can get mashup plugin ids', function (): void {
expect($mashupItem->getMashupPluginIds())->toBe([$plugin1->id, $plugin2->id]);
});
-test('playlist item can get required plugin count for different layouts', function (): void {
+test('playlist item can get required plugin count for different layouts', function () {
$layouts = [
'1Lx1R' => 2,
'1Tx1B' => 2,
@@ -117,7 +117,7 @@ test('playlist item can get required plugin count for different layouts', functi
}
});
-test('playlist item can get layout type', function (): void {
+test('playlist item can get layout type', function () {
$layoutTypes = [
'1Lx1R' => 'vertical',
'1Lx2R' => 'vertical',
@@ -144,7 +144,7 @@ test('playlist item can get layout type', function (): void {
}
});
-test('playlist item can get layout size for different positions', function (): void {
+test('playlist item can get layout size for different positions', function () {
$plugin1 = Plugin::factory()->create();
$plugin2 = Plugin::factory()->create();
$plugin3 = Plugin::factory()->create();
@@ -163,7 +163,7 @@ test('playlist item can get layout size for different positions', function (): v
->and($mashupItem->getLayoutSize(2))->toBe('half_vertical');
});
-test('playlist item can get available layouts', function (): void {
+test('playlist item can get available layouts', function () {
$layouts = PlaylistItem::getAvailableLayouts();
expect($layouts)->toBeArray()
@@ -171,7 +171,7 @@ test('playlist item can get available layouts', function (): void {
->and($layouts['1Lx1R'])->toBe('1 Left - 1 Right (2 plugins)');
});
-test('playlist item can get required plugin count for layout', function (): void {
+test('playlist item can get required plugin count for layout', function () {
$layouts = [
'1Lx1R' => 2,
'1Tx1B' => 2,
@@ -187,7 +187,7 @@ test('playlist item can get required plugin count for layout', function (): void
}
});
-test('playlist item can create mashup', function (): void {
+test('playlist item can create mashup', function () {
$playlist = Playlist::factory()->create();
$plugins = Plugin::factory()->count(3)->create();
$pluginIds = $plugins->pluck('id')->toArray();
diff --git a/tests/Unit/Models/PlaylistTest.php b/tests/Unit/Models/PlaylistTest.php
index 62d3aaf..55d31c7 100644
--- a/tests/Unit/Models/PlaylistTest.php
+++ b/tests/Unit/Models/PlaylistTest.php
@@ -4,7 +4,7 @@ use App\Models\Device;
use App\Models\Playlist;
use App\Models\PlaylistItem;
-test('playlist has required attributes', function (): void {
+test('playlist has required attributes', function () {
$playlist = Playlist::factory()->create([
'name' => 'Test Playlist',
'is_active' => true,
@@ -21,7 +21,7 @@ test('playlist has required attributes', function (): void {
->active_until->format('H:i')->toBe('17:00');
});
-test('playlist belongs to device', function (): void {
+test('playlist belongs to device', function () {
$device = Device::factory()->create();
$playlist = Playlist::factory()->create(['device_id' => $device->id]);
@@ -30,7 +30,7 @@ test('playlist belongs to device', function (): void {
->id->toBe($device->id);
});
-test('playlist has many items', function (): void {
+test('playlist has many items', function () {
$playlist = Playlist::factory()->create();
$items = PlaylistItem::factory()->count(3)->create(['playlist_id' => $playlist->id]);
@@ -39,7 +39,7 @@ test('playlist has many items', function (): void {
->each->toBeInstanceOf(PlaylistItem::class);
});
-test('getNextPlaylistItem returns null when playlist is inactive', function (): void {
+test('getNextPlaylistItem returns null when playlist is inactive', function () {
$playlist = Playlist::factory()->create(['is_active' => false]);
expect($playlist->getNextPlaylistItem())->toBeNull();
diff --git a/tests/Unit/Models/PluginTest.php b/tests/Unit/Models/PluginTest.php
index aa9a28e..248e6f5 100644
--- a/tests/Unit/Models/PluginTest.php
+++ b/tests/Unit/Models/PluginTest.php
@@ -1,13 +1,11 @@
create([
'name' => 'Test Plugin',
'data_payload' => ['key' => 'value'],
@@ -20,7 +18,7 @@ test('plugin has required attributes', function (): void {
->uuid->toMatch('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/');
});
-test('plugin automatically generates uuid on creation', function (): void {
+test('plugin automatically generates uuid on creation', function () {
$plugin = Plugin::factory()->create();
expect($plugin->uuid)
@@ -28,14 +26,14 @@ test('plugin automatically generates uuid on creation', function (): void {
->toMatch('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/');
});
-test('plugin can have custom uuid', function (): void {
+test('plugin can have custom uuid', function () {
$uuid = Illuminate\Support\Str::uuid();
$plugin = Plugin::factory()->create(['uuid' => $uuid]);
expect($plugin->uuid)->toBe($uuid);
});
-test('plugin data_payload is cast to array', function (): void {
+test('plugin data_payload is cast to array', function () {
$data = ['key' => 'value'];
$plugin = Plugin::factory()->create(['data_payload' => $data]);
@@ -44,7 +42,7 @@ test('plugin data_payload is cast to array', function (): void {
->toBe($data);
});
-test('plugin can have polling body for POST requests', function (): void {
+test('plugin can have polling body for POST requests', function () {
$plugin = Plugin::factory()->create([
'polling_verb' => 'post',
'polling_body' => '{"query": "query { user { id name } }"}',
@@ -53,7 +51,7 @@ test('plugin can have polling body for POST requests', function (): void {
expect($plugin->polling_body)->toBe('{"query": "query { user { id name } }"}');
});
-test('updateDataPayload sends POST request with body when polling_verb is post', function (): void {
+test('updateDataPayload sends POST request with body when polling_verb is post', function () {
Http::fake([
'https://example.com/api' => Http::response(['success' => true], 200),
]);
@@ -67,12 +65,14 @@ test('updateDataPayload sends POST request with body when polling_verb is post',
$plugin->updateDataPayload();
- Http::assertSent(fn ($request): bool => $request->url() === 'https://example.com/api' &&
- $request->method() === 'POST' &&
- $request->body() === '{"query": "query { user { id name } }"}');
+ Http::assertSent(function ($request) {
+ return $request->url() === 'https://example.com/api' &&
+ $request->method() === 'POST' &&
+ $request->body() === '{"query": "query { user { id name } }"}';
+ });
});
-test('updateDataPayload handles multiple URLs with IDX_ prefixes', function (): void {
+test('updateDataPayload handles multiple URLs with IDX_ prefixes', function () {
$plugin = Plugin::factory()->create([
'data_strategy' => 'polling',
'polling_url' => "https://api1.example.com/data\nhttps://api2.example.com/weather\nhttps://api3.example.com/news",
@@ -99,36 +99,7 @@ test('updateDataPayload handles multiple URLs with IDX_ prefixes', function ():
expect($plugin->data_payload['IDX_2'])->toBe(['headline' => 'test']);
});
-test('updateDataPayload skips empty lines in polling_url and maintains sequential IDX keys', function (): void {
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- // empty lines and extra spaces between the URL to generate empty entries
- 'polling_url' => "https://api1.example.com/data\n \n\nhttps://api2.example.com/weather\n ",
- 'polling_verb' => 'get',
- ]);
-
- // Mock only the valid URLs
- Http::fake([
- 'https://api1.example.com/data' => Http::response(['item' => 'first'], 200),
- 'https://api2.example.com/weather' => Http::response(['item' => 'second'], 200),
- ]);
-
- $plugin->updateDataPayload();
-
- // payload should only have 2 items, and they should be indexed 0 and 1
- expect($plugin->data_payload)->toHaveCount(2);
- expect($plugin->data_payload)->toHaveKey('IDX_0');
- expect($plugin->data_payload)->toHaveKey('IDX_1');
-
- // data is correct
- expect($plugin->data_payload['IDX_0'])->toBe(['item' => 'first']);
- expect($plugin->data_payload['IDX_1'])->toBe(['item' => 'second']);
-
- // no empty index exists
- expect($plugin->data_payload)->not->toHaveKey('IDX_2');
-});
-
-test('updateDataPayload handles single URL without nesting', function (): void {
+test('updateDataPayload handles single URL without nesting', function () {
$plugin = Plugin::factory()->create([
'data_strategy' => 'polling',
'polling_url' => 'https://api.example.com/data',
@@ -149,7 +120,7 @@ test('updateDataPayload handles single URL without nesting', function (): void {
expect($plugin->data_payload)->not->toHaveKey('IDX_0');
});
-test('updateDataPayload resolves Liquid variables in polling_header', function (): void {
+test('updateDataPayload resolves Liquid variables in polling_header', function () {
$plugin = Plugin::factory()->create([
'data_strategy' => 'polling',
'polling_url' => 'https://api.example.com/data',
@@ -168,13 +139,15 @@ test('updateDataPayload resolves Liquid variables in polling_header', function (
$plugin->updateDataPayload();
- Http::assertSent(fn ($request): bool => $request->url() === 'https://api.example.com/data' &&
- $request->method() === 'GET' &&
- $request->header('Authorization')[0] === 'Bearer test123' &&
- $request->header('X-Custom-Header')[0] === 'custom_header_value');
+ Http::assertSent(function ($request) {
+ return $request->url() === 'https://api.example.com/data' &&
+ $request->method() === 'GET' &&
+ $request->header('Authorization')[0] === 'Bearer test123' &&
+ $request->header('X-Custom-Header')[0] === 'custom_header_value';
+ });
});
-test('updateDataPayload resolves Liquid variables in polling_body', function (): void {
+test('updateDataPayload resolves Liquid variables in polling_body', function () {
$plugin = Plugin::factory()->create([
'data_strategy' => 'polling',
'polling_url' => 'https://api.example.com/data',
@@ -193,7 +166,7 @@ test('updateDataPayload resolves Liquid variables in polling_body', function ():
$plugin->updateDataPayload();
- Http::assertSent(function ($request): bool {
+ Http::assertSent(function ($request) {
$expectedBody = '{"query": "query { user { id name } }", "api_key": "test123", "user_id": "456"}';
return $request->url() === 'https://api.example.com/data' &&
@@ -202,7 +175,7 @@ test('updateDataPayload resolves Liquid variables in polling_body', function ():
});
});
-test('webhook plugin is stale if webhook event occurred', function (): void {
+test('webhook plugin is stale if webhook event occurred', function () {
$plugin = Plugin::factory()->create([
'data_strategy' => 'webhook',
'data_payload_updated_at' => now()->subMinutes(10),
@@ -213,7 +186,7 @@ test('webhook plugin is stale if webhook event occurred', function (): void {
});
-test('webhook plugin data not stale if no webhook event occurred for 1 hour', function (): void {
+test('webhook plugin data not stale if no webhook event occurred for 1 hour', function () {
$plugin = Plugin::factory()->create([
'data_strategy' => 'webhook',
'data_payload_updated_at' => now()->subMinutes(60),
@@ -224,7 +197,7 @@ test('webhook plugin data not stale if no webhook event occurred for 1 hour', fu
});
-test('plugin configuration is cast to array', function (): void {
+test('plugin configuration is cast to array', function () {
$config = ['timezone' => 'UTC', 'refresh_interval' => 30];
$plugin = Plugin::factory()->create(['configuration' => $config]);
@@ -233,7 +206,7 @@ test('plugin configuration is cast to array', function (): void {
->toBe($config);
});
-test('plugin can get configuration value by key', function (): void {
+test('plugin can get configuration value by key', function () {
$config = ['timezone' => 'UTC', 'refresh_interval' => 30];
$plugin = Plugin::factory()->create(['configuration' => $config]);
@@ -242,7 +215,7 @@ test('plugin can get configuration value by key', function (): void {
expect($plugin->getConfiguration('nonexistent', 'default'))->toBe('default');
});
-test('plugin configuration template is cast to array', function (): void {
+test('plugin configuration template is cast to array', function () {
$template = [
'custom_fields' => [
[
@@ -260,7 +233,7 @@ test('plugin configuration template is cast to array', function (): void {
->toBe($template);
});
-test('resolveLiquidVariables resolves variables from configuration', function (): void {
+test('resolveLiquidVariables resolves variables from configuration', function () {
$plugin = Plugin::factory()->create([
'configuration' => [
'api_key' => '12345',
@@ -290,7 +263,7 @@ test('resolveLiquidVariables resolves variables from configuration', function ()
expect($result)->toBe('High');
});
-test('resolveLiquidVariables handles invalid Liquid syntax gracefully', function (): void {
+test('resolveLiquidVariables handles invalid Liquid syntax gracefully', function () {
$plugin = Plugin::factory()->create([
'configuration' => [
'api_key' => '12345',
@@ -304,7 +277,7 @@ test('resolveLiquidVariables handles invalid Liquid syntax gracefully', function
->toThrow(Keepsuit\Liquid\Exceptions\SyntaxException::class);
});
-test('plugin can extract default values from custom fields configuration template', function (): void {
+test('plugin can extract default values from custom fields configuration template', function () {
$configurationTemplate = [
'custom_fields' => [
[
@@ -350,7 +323,7 @@ test('plugin can extract default values from custom fields configuration templat
expect($plugin->getConfiguration('timezone'))->toBeNull();
});
-test('resolveLiquidVariables resolves configuration variables correctly', function (): void {
+test('resolveLiquidVariables resolves configuration variables correctly', function () {
$plugin = Plugin::factory()->create([
'configuration' => [
'Latitude' => '48.2083',
@@ -365,7 +338,7 @@ test('resolveLiquidVariables resolves configuration variables correctly', functi
expect($plugin->resolveLiquidVariables($template))->toBe($expected);
});
-test('resolveLiquidVariables handles missing variables gracefully', function (): void {
+test('resolveLiquidVariables handles missing variables gracefully', function () {
$plugin = Plugin::factory()->create([
'configuration' => [
'Latitude' => '48.2083',
@@ -378,7 +351,7 @@ test('resolveLiquidVariables handles missing variables gracefully', function ():
expect($plugin->resolveLiquidVariables($template))->toBe($expected);
});
-test('resolveLiquidVariables handles empty configuration', function (): void {
+test('resolveLiquidVariables handles empty configuration', function () {
$plugin = Plugin::factory()->create([
'configuration' => [],
]);
@@ -388,553 +361,3 @@ test('resolveLiquidVariables handles empty configuration', function (): void {
expect($plugin->resolveLiquidVariables($template))->toBe($expected);
});
-
-test('resolveLiquidVariables uses external renderer when preferred_renderer is trmnl-liquid and template contains for loop', function (): void {
- Illuminate\Support\Facades\Process::fake([
- '*' => Illuminate\Support\Facades\Process::result(
- output: 'https://api1.example.com/data\nhttps://api2.example.com/data',
- exitCode: 0
- ),
- ]);
-
- config(['services.trmnl.liquid_enabled' => true]);
- config(['services.trmnl.liquid_path' => '/usr/local/bin/trmnl-liquid-cli']);
-
- $plugin = Plugin::factory()->create([
- 'preferred_renderer' => 'trmnl-liquid',
- 'configuration' => [
- 'recipe_ids' => '1,2',
- ],
- ]);
-
- $template = <<<'LIQUID'
-{% assign ids = recipe_ids | split: "," %}
-{% for id in ids %}
-https://api{{ id }}.example.com/data
-{% endfor %}
-LIQUID;
-
- $result = $plugin->resolveLiquidVariables($template);
-
- // Trim trailing newlines that may be added by the process
- expect(mb_trim($result))->toBe('https://api1.example.com/data\nhttps://api2.example.com/data');
-
- Illuminate\Support\Facades\Process::assertRan(function ($process): bool {
- $command = is_array($process->command) ? implode(' ', $process->command) : $process->command;
-
- return str_contains($command, 'trmnl-liquid-cli') &&
- str_contains($command, '--template') &&
- str_contains($command, '--context');
- });
-});
-
-test('resolveLiquidVariables uses internal renderer when preferred_renderer is not trmnl-liquid', function (): void {
- $plugin = Plugin::factory()->create([
- 'preferred_renderer' => 'php',
- 'configuration' => [
- 'recipe_ids' => '1,2',
- ],
- ]);
-
- $template = <<<'LIQUID'
-{% assign ids = recipe_ids | split: "," %}
-{% for id in ids %}
-https://api{{ id }}.example.com/data
-{% endfor %}
-LIQUID;
-
- // Should use internal renderer even with for loop
- $result = $plugin->resolveLiquidVariables($template);
-
- // Internal renderer should process the template
- expect($result)->toBeString();
-});
-
-test('resolveLiquidVariables uses internal renderer when external renderer is disabled', function (): void {
- config(['services.trmnl.liquid_enabled' => false]);
-
- $plugin = Plugin::factory()->create([
- 'preferred_renderer' => 'trmnl-liquid',
- 'configuration' => [
- 'recipe_ids' => '1,2',
- ],
- ]);
-
- $template = <<<'LIQUID'
-{% assign ids = recipe_ids | split: "," %}
-{% for id in ids %}
-https://api{{ id }}.example.com/data
-{% endfor %}
-LIQUID;
-
- // Should use internal renderer when external is disabled
- $result = $plugin->resolveLiquidVariables($template);
-
- expect($result)->toBeString();
-});
-
-test('resolveLiquidVariables uses internal renderer when template does not contain for loop', function (): void {
- config(['services.trmnl.liquid_enabled' => true]);
- config(['services.trmnl.liquid_path' => '/usr/local/bin/trmnl-liquid-cli']);
-
- $plugin = Plugin::factory()->create([
- 'preferred_renderer' => 'trmnl-liquid',
- 'configuration' => [
- 'api_key' => 'test123',
- ],
- ]);
-
- $template = 'https://api.example.com/data?key={{ api_key }}';
-
- // Should use internal renderer when no for loop
- $result = $plugin->resolveLiquidVariables($template);
-
- expect($result)->toBe('https://api.example.com/data?key=test123');
-
- Illuminate\Support\Facades\Process::assertNothingRan();
-});
-
-test('resolveLiquidVariables detects for loop with standard opening tag', function (): void {
- Illuminate\Support\Facades\Process::fake([
- '*' => Illuminate\Support\Facades\Process::result(
- output: 'resolved',
- exitCode: 0
- ),
- ]);
-
- config(['services.trmnl.liquid_enabled' => true]);
- config(['services.trmnl.liquid_path' => '/usr/local/bin/trmnl-liquid-cli']);
-
- $plugin = Plugin::factory()->create([
- 'preferred_renderer' => 'trmnl-liquid',
- 'configuration' => [],
- ]);
-
- // Test {% for pattern
- $template = '{% for item in items %}test{% endfor %}';
- $plugin->resolveLiquidVariables($template);
-
- Illuminate\Support\Facades\Process::assertRan(function ($process): bool {
- $command = is_array($process->command) ? implode(' ', $process->command) : (string) $process->command;
-
- return str_contains($command, 'trmnl-liquid-cli');
- });
-});
-
-test('resolveLiquidVariables detects for loop with whitespace stripping tag', function (): void {
- Illuminate\Support\Facades\Process::fake([
- '*' => Illuminate\Support\Facades\Process::result(
- output: 'resolved',
- exitCode: 0
- ),
- ]);
-
- config(['services.trmnl.liquid_enabled' => true]);
- config(['services.trmnl.liquid_path' => '/usr/local/bin/trmnl-liquid-cli']);
-
- $plugin = Plugin::factory()->create([
- 'preferred_renderer' => 'trmnl-liquid',
- 'configuration' => [],
- ]);
-
- // Test {%- for pattern (with whitespace stripping)
- $template = '{%- for item in items %}test{% endfor %}';
- $plugin->resolveLiquidVariables($template);
-
- Illuminate\Support\Facades\Process::assertRan(function ($process): bool {
- $command = is_array($process->command) ? implode(' ', $process->command) : (string) $process->command;
-
- return str_contains($command, 'trmnl-liquid-cli');
- });
-});
-
-test('updateDataPayload resolves entire polling_url field first then splits by newline', function (): void {
- Http::fake([
- 'https://api1.example.com/data' => Http::response(['data' => 'test1'], 200),
- 'https://api2.example.com/data' => Http::response(['data' => 'test2'], 200),
- ]);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'polling_url' => "https://api1.example.com/data\nhttps://api2.example.com/data",
- 'polling_verb' => 'get',
- 'configuration' => [
- 'recipe_ids' => '1,2',
- ],
- ]);
-
- $plugin->updateDataPayload();
-
- // Should have split the multi-line URL and generated two requests
- expect($plugin->data_payload)->toHaveKey('IDX_0');
- expect($plugin->data_payload)->toHaveKey('IDX_1');
- expect($plugin->data_payload['IDX_0'])->toBe(['data' => 'test1']);
- expect($plugin->data_payload['IDX_1'])->toBe(['data' => 'test2']);
-});
-
-test('updateDataPayload handles multi-line polling_url with for loop using external renderer', function (): void {
- Illuminate\Support\Facades\Process::fake([
- '*' => Illuminate\Support\Facades\Process::result(
- output: "https://api1.example.com/data\nhttps://api2.example.com/data",
- exitCode: 0
- ),
- ]);
-
- Http::fake([
- 'https://api1.example.com/data' => Http::response(['data' => 'test1'], 200),
- 'https://api2.example.com/data' => Http::response(['data' => 'test2'], 200),
- ]);
-
- config(['services.trmnl.liquid_enabled' => true]);
- config(['services.trmnl.liquid_path' => '/usr/local/bin/trmnl-liquid-cli']);
-
- $plugin = Plugin::factory()->create([
- 'data_strategy' => 'polling',
- 'preferred_renderer' => 'trmnl-liquid',
- 'polling_url' => <<<'LIQUID'
-{% assign ids = recipe_ids | split: "," %}
-{% for id in ids %}
-https://api{{ id }}.example.com/data
-{% endfor %}
-LIQUID
- ,
- 'polling_verb' => 'get',
- 'configuration' => [
- 'recipe_ids' => '1,2',
- ],
- ]);
-
- $plugin->updateDataPayload();
-
- // Should have used external renderer and generated two URLs
- expect($plugin->data_payload)->toHaveKey('IDX_0');
- expect($plugin->data_payload)->toHaveKey('IDX_1');
- expect($plugin->data_payload['IDX_0'])->toBe(['data' => 'test1']);
- expect($plugin->data_payload['IDX_1'])->toBe(['data' => 'test2']);
-
- Illuminate\Support\Facades\Process::assertRan(function ($process): bool {
- $command = is_array($process->command) ? implode(' ', $process->command) : (string) $process->command;
-
- return str_contains($command, 'trmnl-liquid-cli');
- });
-});
-
-test('plugin render uses user timezone when set', function (): void {
- $user = User::factory()->create([
- 'timezone' => 'America/New_York',
- ]);
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'markup_language' => 'liquid',
- 'render_markup' => '{{ trmnl.user.time_zone_iana }}',
- ]);
-
- $rendered = $plugin->render();
-
- expect($rendered)->toContain('America/New_York');
-});
-
-test('plugin render falls back to app timezone when user timezone is not set', function (): void {
- $user = User::factory()->create([
- 'timezone' => null,
- ]);
-
- config(['app.timezone' => 'Europe/London']);
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'markup_language' => 'liquid',
- 'render_markup' => '{{ trmnl.user.time_zone_iana }}',
- ]);
-
- $rendered = $plugin->render();
-
- expect($rendered)->toContain('Europe/London');
-});
-
-test('plugin render calculates correct UTC offset from user timezone', function (): void {
- $user = User::factory()->create([
- 'timezone' => 'America/New_York', // UTC-5 (EST) or UTC-4 (EDT)
- ]);
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'markup_language' => 'liquid',
- 'render_markup' => '{{ trmnl.user.utc_offset }}',
- ]);
-
- $rendered = $plugin->render();
-
- // America/New_York offset should be -18000 (EST) or -14400 (EDT) in seconds
- $expectedOffset = (string) Carbon::now('America/New_York')->getOffset();
- expect($rendered)->toContain($expectedOffset);
-});
-
-test('plugin render calculates correct UTC offset from app timezone when user timezone is null', function (): void {
- $user = User::factory()->create([
- 'timezone' => null,
- ]);
-
- config(['app.timezone' => 'Europe/London']);
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'markup_language' => 'liquid',
- 'render_markup' => '{{ trmnl.user.utc_offset }}',
- ]);
-
- $rendered = $plugin->render();
-
- // Europe/London offset should be 0 (GMT) or 3600 (BST) in seconds
- $expectedOffset = (string) Carbon::now('Europe/London')->getOffset();
- expect($rendered)->toContain($expectedOffset);
-});
-
-test('plugin render includes utc_offset and time_zone_iana in trmnl.user context', function (): void {
- $user = User::factory()->create([
- 'timezone' => 'America/Chicago', // UTC-6 (CST) or UTC-5 (CDT)
- ]);
-
- $plugin = Plugin::factory()->create([
- 'user_id' => $user->id,
- 'markup_language' => 'liquid',
- 'render_markup' => '{{ trmnl.user.time_zone_iana }}|{{ trmnl.user.utc_offset }}',
- ]);
-
- $rendered = $plugin->render();
-
- expect($rendered)
- ->toContain('America/Chicago')
- ->and($rendered)->toMatch('/\|-?\d+/'); // Should contain a pipe followed by a number (offset in seconds)
-});
-
-/**
- * Plugin security: XSS Payload Dataset
- * [Input, Expected Result, Forbidden String]
- */
-dataset('xss_vectors', [
- 'standard_script' => ['Safe ', 'Safe ', '