mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
feat: recipes zip import support, add trmnlp compatible recipe configuration
Some checks are pending
tests / ci (push) Waiting to run
Some checks are pending
tests / ci (push) Waiting to run
* recipes zip import support * add trmnlp compatible recipe configuration * support for multiple polling urls
This commit is contained in:
parent
a927c0fb97
commit
414ca47cbf
17 changed files with 2409 additions and 125 deletions
126
tests/Feature/PluginDefaultValuesTest.php
Normal file
126
tests/Feature/PluginDefaultValuesTest.php
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Plugin;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Volt\Volt;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
test('plugin import extracts default values from custom_fields and stores in configuration', function () {
|
||||
// Create a user
|
||||
$user = User::factory()->create();
|
||||
|
||||
// Test the functionality directly by creating a plugin with the expected configuration
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'reading_days',
|
||||
'field_type' => 'string',
|
||||
'name' => 'Reading Days',
|
||||
'description' => 'Select days of the week to read',
|
||||
'default' => 'Monday,Friday,Saturday,Sunday'
|
||||
],
|
||||
[
|
||||
'keyname' => 'refresh_interval',
|
||||
'field_type' => 'number',
|
||||
'name' => 'Refresh Interval',
|
||||
'description' => 'How often to refresh data',
|
||||
'default' => 15
|
||||
],
|
||||
[
|
||||
'keyname' => 'timezone',
|
||||
'field_type' => 'time_zone',
|
||||
'name' => 'Timezone',
|
||||
'description' => 'Select your timezone'
|
||||
// No default value
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Extract default values from custom_fields and populate configuration
|
||||
$configuration = [];
|
||||
if (isset($configurationTemplate['custom_fields']) && is_array($configurationTemplate['custom_fields'])) {
|
||||
foreach ($configurationTemplate['custom_fields'] as $field) {
|
||||
if (isset($field['keyname']) && isset($field['default'])) {
|
||||
$configuration[$field['keyname']] = $field['default'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the plugin directly
|
||||
$plugin = Plugin::create([
|
||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Test Plugin with Defaults',
|
||||
'data_stale_minutes' => 30,
|
||||
'data_strategy' => 'static',
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => $configuration,
|
||||
]);
|
||||
|
||||
// Assert the plugin was created with correct configuration
|
||||
expect($plugin)->not->toBeNull();
|
||||
expect($plugin->configuration)->toBeArray();
|
||||
expect($plugin->configuration)->toHaveKey('reading_days');
|
||||
expect($plugin->configuration)->toHaveKey('refresh_interval');
|
||||
expect($plugin->configuration)->not->toHaveKey('timezone');
|
||||
|
||||
expect($plugin->getConfiguration('reading_days'))->toBe('Monday,Friday,Saturday,Sunday');
|
||||
expect($plugin->getConfiguration('refresh_interval'))->toBe(15);
|
||||
expect($plugin->getConfiguration('timezone'))->toBeNull();
|
||||
|
||||
// Verify configuration template was stored correctly
|
||||
expect($plugin->configuration_template)->toBeArray();
|
||||
expect($plugin->configuration_template['custom_fields'])->toHaveCount(3);
|
||||
});
|
||||
|
||||
test('plugin import handles custom_fields without default values', function () {
|
||||
// Create a user
|
||||
$user = User::factory()->create();
|
||||
|
||||
// Test the functionality directly by creating a plugin with no default values
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'timezone',
|
||||
'field_type' => 'time_zone',
|
||||
'name' => 'Timezone',
|
||||
'description' => 'Select your timezone'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Extract default values from custom_fields and populate configuration
|
||||
$configuration = [];
|
||||
if (isset($configurationTemplate['custom_fields']) && is_array($configurationTemplate['custom_fields'])) {
|
||||
foreach ($configurationTemplate['custom_fields'] as $field) {
|
||||
if (isset($field['keyname']) && isset($field['default'])) {
|
||||
$configuration[$field['keyname']] = $field['default'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the plugin directly
|
||||
$plugin = Plugin::create([
|
||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Test Plugin No Defaults',
|
||||
'data_stale_minutes' => 30,
|
||||
'data_strategy' => 'static',
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => $configuration,
|
||||
]);
|
||||
|
||||
// Assert the plugin was created with empty configuration
|
||||
expect($plugin)->not->toBeNull();
|
||||
expect($plugin->configuration)->toBeArray();
|
||||
expect($plugin->configuration)->toBeEmpty();
|
||||
|
||||
// Verify configuration template was stored correctly
|
||||
expect($plugin->configuration_template)->toBeArray();
|
||||
expect($plugin->configuration_template['custom_fields'])->toHaveCount(1);
|
||||
});
|
||||
179
tests/Feature/PluginImportTest.php
Normal file
179
tests/Feature/PluginImportTest.php
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\Plugin;
|
||||
use App\Models\User;
|
||||
use App\Services\PluginImportService;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
beforeEach(function () {
|
||||
Storage::fake('local');
|
||||
});
|
||||
|
||||
it('imports plugin from valid zip file', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
// Create a mock ZIP file with the required structure
|
||||
$zipContent = createMockZipFile([
|
||||
'src/settings.yml' => getValidSettingsYaml(),
|
||||
'src/full.liquid' => getValidFullLiquid(),
|
||||
]);
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
$plugin = $pluginImportService->importFromZip($zipFile, $user);
|
||||
|
||||
expect($plugin)->toBeInstanceOf(Plugin::class)
|
||||
->and($plugin->user_id)->toBe($user->id)
|
||||
->and($plugin->name)->toBe('Test Plugin')
|
||||
->and($plugin->data_stale_minutes)->toBe(30)
|
||||
->and($plugin->data_strategy)->toBe('static')
|
||||
->and($plugin->markup_language)->toBe('liquid')
|
||||
->and($plugin->configuration_template)->toHaveKey('custom_fields')
|
||||
->and($plugin->configuration)->toHaveKey('api_key')
|
||||
->and($plugin->configuration['api_key'])->toBe('default-api-key');
|
||||
});
|
||||
|
||||
it('imports plugin with shared.liquid file', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$zipContent = createMockZipFile([
|
||||
'src/settings.yml' => getValidSettingsYaml(),
|
||||
'src/full.liquid' => getValidFullLiquid(),
|
||||
'src/shared.liquid' => '{% comment %}Shared styles{% endcomment %}',
|
||||
]);
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
$plugin = $pluginImportService->importFromZip($zipFile, $user);
|
||||
|
||||
expect($plugin->render_markup)->toContain('{% comment %}Shared styles{% endcomment %}')
|
||||
->and($plugin->render_markup)->toContain('<div class="view view--{{ size }}">');
|
||||
});
|
||||
|
||||
it('imports plugin with files in root directory', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$zipContent = createMockZipFile([
|
||||
'settings.yml' => getValidSettingsYaml(),
|
||||
'full.liquid' => getValidFullLiquid(),
|
||||
]);
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
$plugin = $pluginImportService->importFromZip($zipFile, $user);
|
||||
|
||||
expect($plugin)->toBeInstanceOf(Plugin::class)
|
||||
->and($plugin->name)->toBe('Test Plugin');
|
||||
});
|
||||
|
||||
it('throws exception for invalid zip file', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('invalid.zip', 'not a zip file');
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
expect(fn () => $pluginImportService->importFromZip($zipFile, $user))
|
||||
->toThrow(Exception::class, 'Could not open the ZIP file.');
|
||||
});
|
||||
|
||||
it('throws exception for missing required files', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$zipContent = createMockZipFile([
|
||||
'src/settings.yml' => getValidSettingsYaml(),
|
||||
// Missing full.liquid
|
||||
]);
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
expect(fn () => $pluginImportService->importFromZip($zipFile, $user))
|
||||
->toThrow(Exception::class, 'Invalid ZIP structure. Required files settings.yml and full.liquid/full.blade.php are missing.');
|
||||
});
|
||||
|
||||
it('sets default values when settings are missing', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$zipContent = createMockZipFile([
|
||||
'src/settings.yml' => "name: Minimal Plugin\n",
|
||||
'src/full.liquid' => getValidFullLiquid(),
|
||||
]);
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
$plugin = $pluginImportService->importFromZip($zipFile, $user);
|
||||
|
||||
expect($plugin->name)->toBe('Minimal Plugin')
|
||||
->and($plugin->data_stale_minutes)->toBe(15) // default value
|
||||
->and($plugin->data_strategy)->toBe('static') // default value
|
||||
->and($plugin->polling_verb)->toBe('get'); // default value
|
||||
});
|
||||
|
||||
it('handles blade markup language correctly', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$zipContent = createMockZipFile([
|
||||
'src/settings.yml' => getValidSettingsYaml(),
|
||||
'src/full.blade.php' => '<div>Blade template</div>',
|
||||
]);
|
||||
|
||||
$zipFile = UploadedFile::fake()->createWithContent('test-plugin.zip', $zipContent);
|
||||
|
||||
$pluginImportService = new PluginImportService();
|
||||
$plugin = $pluginImportService->importFromZip($zipFile, $user);
|
||||
|
||||
expect($plugin->markup_language)->toBe('blade');
|
||||
});
|
||||
|
||||
// Helper methods
|
||||
function createMockZipFile(array $files): string
|
||||
{
|
||||
$zip = new ZipArchive();
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'test_zip_');
|
||||
|
||||
$zip->open($tempFile, ZipArchive::CREATE);
|
||||
|
||||
foreach ($files as $path => $content) {
|
||||
$zip->addFromString($path, $content);
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
$content = file_get_contents($tempFile);
|
||||
unlink($tempFile);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
function getValidSettingsYaml(): string
|
||||
{
|
||||
return <<<'YAML'
|
||||
name: Test Plugin
|
||||
refresh_interval: 30
|
||||
strategy: static
|
||||
polling_verb: get
|
||||
static_data: '{"test": "data"}'
|
||||
custom_fields:
|
||||
- keyname: api_key
|
||||
field_type: text
|
||||
default: default-api-key
|
||||
label: API Key
|
||||
YAML;
|
||||
}
|
||||
|
||||
function getValidFullLiquid(): string
|
||||
{
|
||||
return <<<'LIQUID'
|
||||
<div class="plugin-content">
|
||||
<h1>{{ data.title }}</h1>
|
||||
<p>{{ data.description }}</p>
|
||||
</div>
|
||||
LIQUID;
|
||||
}
|
||||
175
tests/Feature/PluginInlineTemplatesTest.php
Normal file
175
tests/Feature/PluginInlineTemplatesTest.php
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Plugin;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PluginInlineTemplatesTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_plugin_with_inline_templates(): void
|
||||
{
|
||||
$plugin = Plugin::factory()->create([
|
||||
'name' => 'Test Plugin',
|
||||
'markup_language' => 'liquid',
|
||||
'render_markup' => <<<'LIQUID'
|
||||
{% assign min = 1 %}
|
||||
{% assign max = facts | size %}
|
||||
{% assign diff = max | minus: min %}
|
||||
{% assign randomNumber = "now" | date: "u" | modulo: diff | plus: min %}
|
||||
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% template title_bar %}
|
||||
<div class="title_bar">
|
||||
<img class="image" src="https://res.jwq.lol/img/lumon.svg">
|
||||
<span class="title">{{ trmnl.plugin_settings.instance_name }}</span>
|
||||
<span class="instance">{{ instance }}</span>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
<div class="view view--{{ size }}">
|
||||
{% render "session",
|
||||
trmnl: trmnl,
|
||||
facts: facts,
|
||||
randomNumber: randomNumber,
|
||||
size_mod: ""
|
||||
%}
|
||||
|
||||
{% render "title_bar",
|
||||
trmnl: trmnl,
|
||||
instance: "Please try to enjoy each fact equally."
|
||||
%}
|
||||
</div>
|
||||
LIQUID
|
||||
,
|
||||
'data_payload' => [
|
||||
'facts' => ['Fact 1', 'Fact 2', 'Fact 3'],
|
||||
],
|
||||
]);
|
||||
|
||||
$result = $plugin->render('full');
|
||||
|
||||
// Should render both templates
|
||||
// Check for any of the facts (since random number generation is non-deterministic)
|
||||
$this->assertTrue(
|
||||
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);
|
||||
}
|
||||
|
||||
public function test_plugin_with_inline_templates_using_with_syntax(): void
|
||||
{
|
||||
$plugin = Plugin::factory()->create([
|
||||
'name' => 'Test Plugin',
|
||||
'markup_language' => 'liquid',
|
||||
'render_markup' => <<<'LIQUID'
|
||||
{% assign min = 1 %}
|
||||
{% assign max = facts | size %}
|
||||
{% assign diff = max | minus: min %}
|
||||
{% assign randomNumber = "now" | date: "u" | modulo: diff | plus: min %}
|
||||
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% template title_bar %}
|
||||
<div class="title_bar">
|
||||
<img class="image" src="https://res.jwq.lol/img/lumon.svg">
|
||||
<span class="title">{{ trmnl.plugin_settings.instance_name }}</span>
|
||||
<span class="instance">{{ instance }}</span>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
<div class="view view--{{ size }}">
|
||||
{% render "session" with
|
||||
trmnl: trmnl,
|
||||
facts: facts,
|
||||
randomNumber: randomNumber,
|
||||
size_mod: ""
|
||||
%}
|
||||
|
||||
{% render "title_bar" with
|
||||
trmnl: trmnl,
|
||||
instance: "Please try to enjoy each fact equally."
|
||||
%}
|
||||
</div>
|
||||
LIQUID
|
||||
,
|
||||
'data_payload' => [
|
||||
'facts' => ['Fact 1', 'Fact 2', 'Fact 3'],
|
||||
],
|
||||
]);
|
||||
|
||||
$result = $plugin->render('full');
|
||||
|
||||
// Should render both templates
|
||||
// Check for any of the facts (since random number generation is non-deterministic)
|
||||
$this->assertTrue(
|
||||
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);
|
||||
}
|
||||
|
||||
public function test_plugin_with_simple_inline_template(): void
|
||||
{
|
||||
$plugin = Plugin::factory()->create([
|
||||
'markup_language' => 'liquid',
|
||||
'render_markup' => <<<'LIQUID'
|
||||
{% template simple %}
|
||||
<div class="simple">
|
||||
<h1>{{ title }}</h1>
|
||||
<p>{{ content }}</p>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% render "simple",
|
||||
title: "Hello World",
|
||||
content: "This is a test"
|
||||
%}
|
||||
LIQUID
|
||||
,
|
||||
]);
|
||||
|
||||
$result = $plugin->render('full');
|
||||
|
||||
$this->assertStringContainsString('Hello World', $result);
|
||||
$this->assertStringContainsString('This is a test', $result);
|
||||
$this->assertStringContainsString('class="simple"', $result);
|
||||
}
|
||||
}
|
||||
218
tests/Feature/PluginRequiredConfigurationTest.php
Normal file
218
tests/Feature/PluginRequiredConfigurationTest.php
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Plugin;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns true when required field is null', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'api_key',
|
||||
'field_type' => 'string',
|
||||
'name' => 'API Key',
|
||||
'description' => 'Your API key',
|
||||
// Not marked as optional, so it's required
|
||||
],
|
||||
[
|
||||
'keyname' => 'timezone',
|
||||
'field_type' => 'time_zone',
|
||||
'name' => 'Timezone',
|
||||
'description' => 'Select your timezone',
|
||||
'optional' => true // Marked as optional
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'timezone' => 'UTC' // Only timezone is set, api_key is missing
|
||||
]
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns false when all required fields are set', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'api_key',
|
||||
'field_type' => 'string',
|
||||
'name' => 'API Key',
|
||||
'description' => 'Your API key',
|
||||
// Not marked as optional, so it's required
|
||||
],
|
||||
[
|
||||
'keyname' => 'timezone',
|
||||
'field_type' => 'time_zone',
|
||||
'name' => 'Timezone',
|
||||
'description' => 'Select your timezone',
|
||||
'optional' => true // Marked as optional
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'api_key' => 'test-api-key', // Required field is set
|
||||
'timezone' => 'UTC'
|
||||
]
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns false when no custom fields exist', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => [],
|
||||
'configuration' => []
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns true when explicitly required field is null', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'api_key',
|
||||
'field_type' => 'string',
|
||||
'name' => 'API Key',
|
||||
'description' => 'Your API key',
|
||||
// Not marked as optional, so it's required
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'api_key' => null // Explicitly set to null
|
||||
]
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns true when required field is empty string', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'api_key',
|
||||
'field_type' => 'string',
|
||||
'name' => 'API Key',
|
||||
'description' => 'Your API key',
|
||||
// Not marked as optional, so it's required
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'api_key' => '' // Empty string
|
||||
]
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns true when required array field is empty', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'selected_items',
|
||||
'field_type' => 'select',
|
||||
'name' => 'Selected Items',
|
||||
'description' => 'Select items',
|
||||
'multiple' => true,
|
||||
// Not marked as optional, so it's required
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'selected_items' => [] // Empty array
|
||||
]
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeTrue();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns false when author_bio field is present but other required field is set', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'author_bio',
|
||||
'name' => 'About This Plugin',
|
||||
'field_type' => 'author_bio',
|
||||
],
|
||||
[
|
||||
'keyname' => 'plugin_field',
|
||||
'name' => 'Field Name',
|
||||
'field_type' => 'string',
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'plugin_field' => 'set' // Required field is set
|
||||
]
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
|
||||
});
|
||||
|
||||
test('hasMissingRequiredConfigurationFields returns false when field has default value', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'api_key',
|
||||
'field_type' => 'string',
|
||||
'name' => 'API Key',
|
||||
'description' => 'Your API key',
|
||||
'default' => 'default-api-key' // Has default value
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [] // Empty configuration, but field has default
|
||||
]);
|
||||
|
||||
expect($plugin->hasMissingRequiredConfigurationFields())->toBeFalse();
|
||||
});
|
||||
297
tests/Unit/Liquid/InlineTemplatesTest.php
Normal file
297
tests/Unit/Liquid/InlineTemplatesTest.php
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Liquid;
|
||||
|
||||
use App\Liquid\FileSystems\InlineTemplatesFileSystem;
|
||||
use App\Liquid\Tags\TemplateTag;
|
||||
use Keepsuit\Liquid\Environment;
|
||||
use Keepsuit\Liquid\Exceptions\LiquidException;
|
||||
use Keepsuit\Liquid\Tags\RenderTag;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class InlineTemplatesTest extends TestCase
|
||||
{
|
||||
protected Environment $environment;
|
||||
|
||||
protected InlineTemplatesFileSystem $fileSystem;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->fileSystem = new InlineTemplatesFileSystem();
|
||||
$this->environment = new Environment(
|
||||
fileSystem: $this->fileSystem
|
||||
);
|
||||
$this->environment->tagRegistry->register(TemplateTag::class);
|
||||
$this->environment->tagRegistry->register(RenderTag::class);
|
||||
}
|
||||
|
||||
public function test_template_tag_registers_template(): void
|
||||
{
|
||||
$template = $this->environment->parseString(<<<'LIQUID'
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
LIQUID
|
||||
);
|
||||
|
||||
$context = $this->environment->newRenderContext(
|
||||
data: [
|
||||
'facts' => ['Fact 1', 'Fact 2', 'Fact 3'],
|
||||
'randomNumber' => 1,
|
||||
'size_mod' => '--large',
|
||||
]
|
||||
);
|
||||
|
||||
$result = $template->render($context);
|
||||
|
||||
// Template tag should not output anything
|
||||
$this->assertEquals('', $result);
|
||||
|
||||
// Template should be registered in the file system
|
||||
$this->assertTrue($this->fileSystem->hasTemplate('session'));
|
||||
|
||||
$registeredTemplate = $this->fileSystem->readTemplateFile('session');
|
||||
$this->assertStringContainsString('{{ facts[randomNumber] }}', $registeredTemplate);
|
||||
$this->assertStringContainsString('{{ size_mod }}', $registeredTemplate);
|
||||
}
|
||||
|
||||
public function test_template_tag_with_render_tag(): void
|
||||
{
|
||||
$template = $this->environment->parseString(<<<'LIQUID'
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% render "session",
|
||||
trmnl: trmnl,
|
||||
facts: facts,
|
||||
randomNumber: randomNumber,
|
||||
size_mod: ""
|
||||
%}
|
||||
LIQUID
|
||||
);
|
||||
|
||||
$context = $this->environment->newRenderContext(
|
||||
data: [
|
||||
'facts' => ['Fact 1', 'Fact 2', 'Fact 3'],
|
||||
'randomNumber' => 1,
|
||||
'trmnl' => ['plugin_settings' => ['instance_name' => 'Test']],
|
||||
]
|
||||
);
|
||||
|
||||
$result = $template->render($context);
|
||||
|
||||
// Should render the template content
|
||||
$this->assertStringContainsString('Fact 2', $result); // facts[1]
|
||||
$this->assertStringContainsString('class="layout"', $result);
|
||||
$this->assertStringContainsString('class="value text--center"', $result);
|
||||
}
|
||||
|
||||
public function test_apply_liquid_replacements_converts_with_syntax(): void
|
||||
{
|
||||
// This test simulates the applyLiquidReplacements method from the Plugin model
|
||||
$originalLiquid = <<<'LIQUID'
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% render "session" with
|
||||
trmnl: trmnl,
|
||||
facts: facts,
|
||||
randomNumber: randomNumber,
|
||||
size_mod: ""
|
||||
%}
|
||||
LIQUID;
|
||||
|
||||
// Apply the same replacement logic as in Plugin::applyLiquidReplacements
|
||||
$convertedLiquid = preg_replace(
|
||||
'/{%\s*render\s+([^}]+?)\s+with\s+/i',
|
||||
'{% render $1, ',
|
||||
$originalLiquid
|
||||
);
|
||||
|
||||
// Verify the conversion worked
|
||||
$this->assertStringContainsString('{% render "session",', $convertedLiquid);
|
||||
$this->assertStringNotContainsString('{% render "session" with', $convertedLiquid);
|
||||
|
||||
// Verify the rest of the content is preserved
|
||||
$this->assertStringContainsString('trmnl: trmnl,', $convertedLiquid);
|
||||
$this->assertStringContainsString('facts: facts,', $convertedLiquid);
|
||||
}
|
||||
|
||||
public function test_template_tag_with_render_with_tag(): void
|
||||
{
|
||||
$originalLiquid = <<<'LIQUID'
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% render "session" with
|
||||
trmnl: trmnl,
|
||||
facts: facts,
|
||||
randomNumber: randomNumber,
|
||||
size_mod: ""
|
||||
%}
|
||||
LIQUID;
|
||||
|
||||
// Apply the same replacement logic as in applyLiquidReplacements
|
||||
$convertedLiquid = preg_replace(
|
||||
'/{%\s*render\s+([^}]+?)\s+with\s+/i',
|
||||
'{% render $1, ',
|
||||
$originalLiquid
|
||||
);
|
||||
|
||||
$template = $this->environment->parseString($convertedLiquid);
|
||||
|
||||
$context = $this->environment->newRenderContext(
|
||||
data: [
|
||||
'facts' => ['Fact 1', 'Fact 2', 'Fact 3'],
|
||||
'randomNumber' => 1,
|
||||
'trmnl' => ['plugin_settings' => ['instance_name' => 'Test']],
|
||||
]
|
||||
);
|
||||
|
||||
$result = $template->render($context);
|
||||
|
||||
// Should render the template content
|
||||
$this->assertStringContainsString('Fact 2', $result); // facts[1]
|
||||
$this->assertStringContainsString('class="layout"', $result);
|
||||
$this->assertStringContainsString('class="value text--center"', $result);
|
||||
}
|
||||
|
||||
public function test_template_tag_with_multiple_templates(): void
|
||||
{
|
||||
$template = $this->environment->parseString(<<<'LIQUID'
|
||||
{% template session %}
|
||||
<div class="layout">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="markdown gap--large">
|
||||
<div class="value{{ size_mod }} text--center">
|
||||
{{ facts[randomNumber] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
{% template title_bar %}
|
||||
<div class="title_bar">
|
||||
<img class="image" src="https://res.jwq.lol/img/lumon.svg">
|
||||
<span class="title">{{ trmnl.plugin_settings.instance_name }}</span>
|
||||
<span class="instance">{{ instance }}</span>
|
||||
</div>
|
||||
{% endtemplate %}
|
||||
|
||||
<div class="view view--{{ size }}">
|
||||
{% render "session",
|
||||
trmnl: trmnl,
|
||||
facts: facts,
|
||||
randomNumber: randomNumber,
|
||||
size_mod: ""
|
||||
%}
|
||||
|
||||
{% render "title_bar",
|
||||
trmnl: trmnl,
|
||||
instance: "Please try to enjoy each fact equally."
|
||||
%}
|
||||
</div>
|
||||
LIQUID
|
||||
);
|
||||
|
||||
$context = $this->environment->newRenderContext(
|
||||
data: [
|
||||
'size' => 'full',
|
||||
'facts' => ['Fact 1', 'Fact 2', 'Fact 3'],
|
||||
'randomNumber' => 1,
|
||||
'trmnl' => ['plugin_settings' => ['instance_name' => 'Test Plugin']],
|
||||
]
|
||||
);
|
||||
|
||||
$result = $template->render($context);
|
||||
|
||||
// Should render both templates
|
||||
$this->assertStringContainsString('Fact 2', $result);
|
||||
$this->assertStringContainsString('Test Plugin', $result);
|
||||
$this->assertStringContainsString('Please try to enjoy each fact equally', $result);
|
||||
$this->assertStringContainsString('class="view view--full"', $result);
|
||||
}
|
||||
|
||||
public function test_template_tag_invalid_name(): void
|
||||
{
|
||||
$this->expectException(LiquidException::class);
|
||||
|
||||
$template = $this->environment->parseString(<<<'LIQUID'
|
||||
{% template invalid-name %}
|
||||
<div>Content</div>
|
||||
{% endtemplate %}
|
||||
LIQUID
|
||||
);
|
||||
|
||||
$context = $this->environment->newRenderContext();
|
||||
|
||||
$template->render($context);
|
||||
}
|
||||
|
||||
public function test_template_tag_without_file_system(): void
|
||||
{
|
||||
$template = $this->environment->parseString(<<<'LIQUID'
|
||||
{% template session %}
|
||||
<div>Content</div>
|
||||
{% endtemplate %}
|
||||
LIQUID
|
||||
);
|
||||
|
||||
$context = $this->environment->newRenderContext();
|
||||
|
||||
$result = $template->render($context);
|
||||
|
||||
// Should not throw an error and should return empty string
|
||||
$this->assertEquals('', $result);
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,109 @@ test('updateDataPayload sends POST request with body when polling_verb is post',
|
|||
});
|
||||
});
|
||||
|
||||
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",
|
||||
'polling_verb' => 'get',
|
||||
'configuration' => [
|
||||
'api_key' => 'test123',
|
||||
],
|
||||
]);
|
||||
|
||||
// Mock HTTP responses
|
||||
Http::fake([
|
||||
'https://api1.example.com/data' => Http::response(['data' => 'test1'], 200),
|
||||
'https://api2.example.com/weather' => Http::response(['temp' => 25], 200),
|
||||
'https://api3.example.com/news' => Http::response(['headline' => 'test'], 200),
|
||||
]);
|
||||
|
||||
$plugin->updateDataPayload();
|
||||
|
||||
expect($plugin->data_payload)->toHaveKey('IDX_0');
|
||||
expect($plugin->data_payload)->toHaveKey('IDX_1');
|
||||
expect($plugin->data_payload)->toHaveKey('IDX_2');
|
||||
expect($plugin->data_payload['IDX_0'])->toBe(['data' => 'test1']);
|
||||
expect($plugin->data_payload['IDX_1'])->toBe(['temp' => 25]);
|
||||
expect($plugin->data_payload['IDX_2'])->toBe(['headline' => 'test']);
|
||||
});
|
||||
|
||||
test('updateDataPayload handles single URL without nesting', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'data_strategy' => 'polling',
|
||||
'polling_url' => 'https://api.example.com/data',
|
||||
'polling_verb' => 'get',
|
||||
'configuration' => [
|
||||
'api_key' => 'test123',
|
||||
],
|
||||
]);
|
||||
|
||||
// Mock HTTP response
|
||||
Http::fake([
|
||||
'https://api.example.com/data' => Http::response(['data' => 'test'], 200),
|
||||
]);
|
||||
|
||||
$plugin->updateDataPayload();
|
||||
|
||||
expect($plugin->data_payload)->toBe(['data' => 'test']);
|
||||
expect($plugin->data_payload)->not->toHaveKey('IDX_0');
|
||||
});
|
||||
|
||||
test('updateDataPayload resolves Liquid variables in polling_header', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'data_strategy' => 'polling',
|
||||
'polling_url' => 'https://api.example.com/data',
|
||||
'polling_verb' => 'get',
|
||||
'polling_header' => "Authorization: Bearer {{ api_key }}\nX-Custom-Header: {{ custom_value }}",
|
||||
'configuration' => [
|
||||
'api_key' => 'test123',
|
||||
'custom_value' => 'custom_header_value',
|
||||
],
|
||||
]);
|
||||
|
||||
// Mock HTTP response
|
||||
Http::fake([
|
||||
'https://api.example.com/data' => Http::response(['data' => 'test'], 200),
|
||||
]);
|
||||
|
||||
$plugin->updateDataPayload();
|
||||
|
||||
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 () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'data_strategy' => 'polling',
|
||||
'polling_url' => 'https://api.example.com/data',
|
||||
'polling_verb' => 'post',
|
||||
'polling_body' => '{"query": "query { user { id name } }", "api_key": "{{ api_key }}", "user_id": "{{ user_id }}"}',
|
||||
'configuration' => [
|
||||
'api_key' => 'test123',
|
||||
'user_id' => '456',
|
||||
],
|
||||
]);
|
||||
|
||||
// Mock HTTP response
|
||||
Http::fake([
|
||||
'https://api.example.com/data' => Http::response(['data' => 'test'], 200),
|
||||
]);
|
||||
|
||||
$plugin->updateDataPayload();
|
||||
|
||||
Http::assertSent(function ($request) {
|
||||
$expectedBody = '{"query": "query { user { id name } }", "api_key": "test123", "user_id": "456"}';
|
||||
|
||||
return $request->url() === 'https://api.example.com/data' &&
|
||||
$request->method() === 'POST' &&
|
||||
$request->body() === $expectedBody;
|
||||
});
|
||||
});
|
||||
|
||||
test('webhook plugin is stale if webhook event occurred', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'data_strategy' => 'webhook',
|
||||
|
|
@ -93,3 +196,168 @@ test('webhook plugin data not stale if no webhook event occurred for 1 hour', fu
|
|||
expect($plugin->isDataStale())->toBeFalse();
|
||||
|
||||
});
|
||||
|
||||
test('plugin configuration is cast to array', function () {
|
||||
$config = ['timezone' => 'UTC', 'refresh_interval' => 30];
|
||||
$plugin = Plugin::factory()->create(['configuration' => $config]);
|
||||
|
||||
expect($plugin->configuration)
|
||||
->toBeArray()
|
||||
->toBe($config);
|
||||
});
|
||||
|
||||
test('plugin can get configuration value by key', function () {
|
||||
$config = ['timezone' => 'UTC', 'refresh_interval' => 30];
|
||||
$plugin = Plugin::factory()->create(['configuration' => $config]);
|
||||
|
||||
expect($plugin->getConfiguration('timezone'))->toBe('UTC');
|
||||
expect($plugin->getConfiguration('refresh_interval'))->toBe(30);
|
||||
expect($plugin->getConfiguration('nonexistent', 'default'))->toBe('default');
|
||||
});
|
||||
|
||||
test('plugin configuration template is cast to array', function () {
|
||||
$template = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'name' => 'Timezone',
|
||||
'keyname' => 'timezone',
|
||||
'field_type' => 'time_zone',
|
||||
'description' => 'Select your timezone',
|
||||
],
|
||||
],
|
||||
];
|
||||
$plugin = Plugin::factory()->create(['configuration_template' => $template]);
|
||||
|
||||
expect($plugin->configuration_template)
|
||||
->toBeArray()
|
||||
->toBe($template);
|
||||
});
|
||||
|
||||
test('resolveLiquidVariables resolves variables from configuration', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'configuration' => [
|
||||
'api_key' => '12345',
|
||||
'username' => 'testuser',
|
||||
'count' => 42,
|
||||
],
|
||||
]);
|
||||
|
||||
// Test simple variable replacement
|
||||
$template = 'API Key: {{ api_key }}';
|
||||
$result = $plugin->resolveLiquidVariables($template);
|
||||
expect($result)->toBe('API Key: 12345');
|
||||
|
||||
// Test multiple variables
|
||||
$template = 'User: {{ username }}, Count: {{ count }}';
|
||||
$result = $plugin->resolveLiquidVariables($template);
|
||||
expect($result)->toBe('User: testuser, Count: 42');
|
||||
|
||||
// Test with missing variable (should keep original)
|
||||
$template = 'Missing: {{ missing }}';
|
||||
$result = $plugin->resolveLiquidVariables($template);
|
||||
expect($result)->toBe('Missing: ');
|
||||
|
||||
// Test with Liquid control structures
|
||||
$template = '{% if count > 40 %}High{% else %}Low{% endif %}';
|
||||
$result = $plugin->resolveLiquidVariables($template);
|
||||
expect($result)->toBe('High');
|
||||
});
|
||||
|
||||
test('resolveLiquidVariables handles invalid Liquid syntax gracefully', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'configuration' => [
|
||||
'api_key' => '12345',
|
||||
],
|
||||
]);
|
||||
|
||||
// Test with unclosed Liquid tag (should throw exception)
|
||||
$template = 'Unclosed tag: {{ config.api_key';
|
||||
|
||||
expect(fn () => $plugin->resolveLiquidVariables($template))
|
||||
->toThrow(Keepsuit\Liquid\Exceptions\SyntaxException::class);
|
||||
});
|
||||
|
||||
test('plugin can extract default values from custom fields configuration template', function () {
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => [
|
||||
[
|
||||
'keyname' => 'reading_days',
|
||||
'field_type' => 'string',
|
||||
'name' => 'Reading Days',
|
||||
'description' => 'Select days of the week to read',
|
||||
'default' => 'Monday,Friday,Saturday,Sunday',
|
||||
],
|
||||
[
|
||||
'keyname' => 'refresh_interval',
|
||||
'field_type' => 'number',
|
||||
'name' => 'Refresh Interval',
|
||||
'description' => 'How often to refresh data',
|
||||
'default' => 30,
|
||||
],
|
||||
[
|
||||
'keyname' => 'timezone',
|
||||
'field_type' => 'time_zone',
|
||||
'name' => 'Timezone',
|
||||
'description' => 'Select your timezone',
|
||||
// No default value
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$plugin = Plugin::factory()->create([
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'configuration' => [
|
||||
'reading_days' => 'Monday,Friday,Saturday,Sunday',
|
||||
'refresh_interval' => 30,
|
||||
],
|
||||
]);
|
||||
|
||||
expect($plugin->configuration)
|
||||
->toBeArray()
|
||||
->toHaveKey('reading_days')
|
||||
->toHaveKey('refresh_interval')
|
||||
->not->toHaveKey('timezone');
|
||||
|
||||
expect($plugin->getConfiguration('reading_days'))->toBe('Monday,Friday,Saturday,Sunday');
|
||||
expect($plugin->getConfiguration('refresh_interval'))->toBe(30);
|
||||
expect($plugin->getConfiguration('timezone'))->toBeNull();
|
||||
});
|
||||
|
||||
test('resolveLiquidVariables resolves configuration variables correctly', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'configuration' => [
|
||||
'Latitude' => '48.2083',
|
||||
'Longitude' => '16.3731',
|
||||
'api_key' => 'test123',
|
||||
],
|
||||
]);
|
||||
|
||||
$template = 'https://suntracker.me/?lat={{ Latitude }}&lon={{ Longitude }}';
|
||||
$expected = 'https://suntracker.me/?lat=48.2083&lon=16.3731';
|
||||
|
||||
expect($plugin->resolveLiquidVariables($template))->toBe($expected);
|
||||
});
|
||||
|
||||
test('resolveLiquidVariables handles missing variables gracefully', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'configuration' => [
|
||||
'Latitude' => '48.2083',
|
||||
],
|
||||
]);
|
||||
|
||||
$template = 'https://suntracker.me/?lat={{ Latitude }}&lon={{ Longitude }}&key={{ api_key }}';
|
||||
$expected = 'https://suntracker.me/?lat=48.2083&lon=&key=';
|
||||
|
||||
expect($plugin->resolveLiquidVariables($template))->toBe($expected);
|
||||
});
|
||||
|
||||
test('resolveLiquidVariables handles empty configuration', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'configuration' => [],
|
||||
]);
|
||||
|
||||
$template = 'https://suntracker.me/?lat={{ Latitude }}&lon={{ Longitude }}';
|
||||
$expected = 'https://suntracker.me/?lat=&lon=';
|
||||
|
||||
expect($plugin->resolveLiquidVariables($template))->toBe($expected);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue