Update
This commit is contained in:
11
tests/Feature/BlockComponentDebugTest.php
Normal file
11
tests/Feature/BlockComponentDebugTest.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
use cPad\Plugins\Blocks\Components\Block;
|
||||
|
||||
it('constructs the block component with debug mode enabled', function () {
|
||||
config()->set('app.debug', true);
|
||||
|
||||
$component = new Block('sample-block');
|
||||
|
||||
expect($component->debug)->toBeTrue();
|
||||
});
|
||||
54
tests/Feature/BlockComponentRenderTest.php
Normal file
54
tests/Feature/BlockComponentRenderTest.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use cPad\Plugins\Blocks\Components\Block as BlockComponent;
|
||||
|
||||
it('renders block html exactly as stored', function () {
|
||||
View::addNamespace('plugin.block', base_path('packages/klevze/Plugins/Blocks/Resources/views'));
|
||||
|
||||
config()->set('app.debug', false);
|
||||
app()->setLocale('en');
|
||||
|
||||
if (! Schema::hasTable('blocks')) {
|
||||
Schema::create('blocks', function (Blueprint $table) {
|
||||
$table->increments('block_id');
|
||||
$table->string('keycode', 64)->unique();
|
||||
$table->text('attributes')->nullable();
|
||||
$table->text('notes')->nullable();
|
||||
$table->boolean('active')->default(true);
|
||||
$table->string('block_group', 16)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
$keycode = 'test-block-render-html';
|
||||
$html = '<section class="hero"><h1>Title</h1><p><strong>Bold</strong> <em>Italic</em></p><svg><path d="M0 0"></path></svg></section>';
|
||||
|
||||
DB::table('blocks')->where('keycode', $keycode)->delete();
|
||||
DB::table('blocks')->insert([
|
||||
'keycode' => $keycode,
|
||||
'attributes' => json_encode([
|
||||
app()->getLocale() => [
|
||||
'content' => $html,
|
||||
],
|
||||
]),
|
||||
'notes' => null,
|
||||
'active' => true,
|
||||
'block_group' => null,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
try {
|
||||
$output = (new BlockComponent($keycode))->render()->render();
|
||||
|
||||
expect(trim($output))->toBe($html);
|
||||
expect($output)->not->toContain('<');
|
||||
expect($output)->not->toContain('&');
|
||||
} finally {
|
||||
DB::table('blocks')->where('keycode', $keycode)->delete();
|
||||
}
|
||||
});
|
||||
80
tests/Feature/BlockEditEncodingTest.php
Normal file
80
tests/Feature/BlockEditEncodingTest.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Klevze\ControlPanel\Facades\ActiveLanguage;
|
||||
use cPad\Plugins\Blocks\Controllers\BlockController;
|
||||
|
||||
it('renders decoded html in the block editor form', function () {
|
||||
View::addNamespace('plugin.block', base_path('packages/klevze/Plugins/Blocks/Resources/views'));
|
||||
|
||||
$decodeHtml = function ($value) {
|
||||
$decoded = (string) $value;
|
||||
|
||||
while (true) {
|
||||
$next = html_entity_decode($decoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
if ($next === $decoded) {
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
$decoded = $next;
|
||||
}
|
||||
};
|
||||
|
||||
$defaultLanguageContent = $decodeHtml('&lt;a href=&quot;#&quot;&gt;Link&lt;/a&gt;');
|
||||
|
||||
$html = view('plugin.block::form.translation', [
|
||||
'languages' => [(object) ['iso' => 'en']],
|
||||
'attributes' => (object) [
|
||||
'en' => (object) [
|
||||
'content' => '&lt;a href=&quot;#&quot;&gt;Link&lt;/a&gt;',
|
||||
'html_code' => null,
|
||||
],
|
||||
],
|
||||
'decodeHtml' => $decodeHtml,
|
||||
'defaultLanguageContent' => $defaultLanguageContent,
|
||||
])->render();
|
||||
|
||||
expect($html)->toContain('"#"');
|
||||
expect($html)->not->toContain('&quot;#&quot;');
|
||||
});
|
||||
|
||||
it('normalizes encoded html before persisting a block', function () {
|
||||
$controller = app(BlockController::class);
|
||||
|
||||
$reflection = new ReflectionMethod($controller, 'normalizePrevodPayload');
|
||||
$reflection->setAccessible(true);
|
||||
|
||||
$normalized = $reflection->invoke($controller, [
|
||||
'en' => [
|
||||
'content' => '&lt;a href=&quot;#&quot;&gt;Link&lt;/a&gt;',
|
||||
],
|
||||
]);
|
||||
|
||||
expect($normalized['en']['content'])->toBe('<a href="#">Link</a>');
|
||||
});
|
||||
|
||||
it('renders frontend language tabs in the block editor', function () {
|
||||
View::addNamespace('plugin.block', base_path('packages/klevze/Plugins/Blocks/Resources/views'));
|
||||
|
||||
ActiveLanguage::shouldReceive('getList')
|
||||
->once()
|
||||
->with('fp')
|
||||
->andReturn(collect([
|
||||
(object) ['iso' => 'en', 'name' => 'English'],
|
||||
(object) ['iso' => 'sl', 'name' => 'Slovenian'],
|
||||
]));
|
||||
|
||||
$html = view('plugin.block::form.translation', [
|
||||
'languages' => [(object) ['iso' => 'en']],
|
||||
'attributes' => (object) [
|
||||
'en' => (object) ['content' => '', 'html_code' => null],
|
||||
],
|
||||
'decodeHtml' => fn ($value) => (string) $value,
|
||||
'defaultLanguageContent' => '',
|
||||
])->render();
|
||||
|
||||
expect($html)->toContain('nav nav-tabs');
|
||||
expect($html)->toContain('English');
|
||||
expect($html)->toContain('Slovenian');
|
||||
});
|
||||
40
tests/Feature/BlockInsertValidationRedirectTest.php
Normal file
40
tests/Feature/BlockInsertValidationRedirectTest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Klevze\ControlPanel\Http\Middleware\InputValidationMiddleware;
|
||||
use Klevze\ControlPanel\Services\Validation\InputValidationService;
|
||||
use function Pest\Laravel\mock;
|
||||
|
||||
it('allows block insert html to bypass generic input validation', function () {
|
||||
mock(InputValidationService::class, function ($mock) {
|
||||
$mock->shouldNotReceive('validateSecurity');
|
||||
});
|
||||
|
||||
$middleware = app(InputValidationMiddleware::class);
|
||||
|
||||
$request = Request::create('/cp/content/blocks/insert', 'POST', [
|
||||
'keycode' => 'hero-block',
|
||||
'notes' => 'Intro section',
|
||||
'block_group' => 'home',
|
||||
'active' => '1',
|
||||
'prevod' => [
|
||||
'en' => [
|
||||
'name' => 'English',
|
||||
'content' => '<script>alert(1)</script>',
|
||||
],
|
||||
],
|
||||
'store_id' => 1,
|
||||
'token_count' => 12,
|
||||
]);
|
||||
|
||||
$request->setRouteResolver(fn () => new class {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'admin.plugin.block.insert';
|
||||
}
|
||||
});
|
||||
|
||||
$response = $middleware->handle($request, fn () => response('ok'));
|
||||
|
||||
expect($response->getContent())->toBe('ok');
|
||||
});
|
||||
31
tests/Feature/BlockLayoutValidationTest.php
Normal file
31
tests/Feature/BlockLayoutValidationTest.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Klevze\ControlPanel\Http\Middleware\InputValidationMiddleware;
|
||||
use Klevze\ControlPanel\Services\Validation\InputValidationService;
|
||||
use function Pest\Laravel\mock;
|
||||
|
||||
it('skips generic input validation for block layout saves', function () {
|
||||
mock(InputValidationService::class, function ($mock) {
|
||||
$mock->shouldNotReceive('validateSecurity');
|
||||
});
|
||||
|
||||
$middleware = app(InputValidationMiddleware::class);
|
||||
|
||||
$request = Request::create('/cp/content/blocks/layout', 'POST', [
|
||||
'header' => '<script src="https://example.com/app.js"></script>',
|
||||
'footer' => '<script>alert(1)</script>',
|
||||
'layout' => 'main',
|
||||
]);
|
||||
|
||||
$request->setRouteResolver(fn () => new class {
|
||||
public function getName(): string
|
||||
{
|
||||
return 'admin.plugin.block.layout.update';
|
||||
}
|
||||
});
|
||||
|
||||
$response = $middleware->handle($request, fn () => response('ok'));
|
||||
|
||||
expect($response->getContent())->toBe('ok');
|
||||
});
|
||||
34
tests/Feature/BlockMainTableTranslationsTest.php
Normal file
34
tests/Feature/BlockMainTableTranslationsTest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
|
||||
it('renders translated block names per language with an n-a fallback', function () {
|
||||
$html = Blade::render(<<<'BLADE'
|
||||
@php
|
||||
$translations = is_array($row->attributes ?? null) ? $row->attributes : [];
|
||||
@endphp
|
||||
@foreach($languages as $lang)
|
||||
@php
|
||||
$translation = $translations[$lang->iso] ?? [];
|
||||
$translatedName = trim((string) data_get($translation, 'name', ''));
|
||||
@endphp
|
||||
<span class="headlines lang-{{ $lang->iso }}">
|
||||
{{ $translatedName !== '' ? $translatedName : 'N/A' }}
|
||||
</span>
|
||||
@endforeach
|
||||
BLADE, [
|
||||
'languages' => collect([
|
||||
(object) ['iso' => 'en', 'name' => 'English'],
|
||||
(object) ['iso' => 'sl', 'name' => 'Slovenian'],
|
||||
]),
|
||||
'row' => (object) [
|
||||
'attributes' => [
|
||||
'en' => ['name' => 'Hero title', 'content' => '<p>Hello</p>'],
|
||||
'sl' => ['content' => '<p>Zivjo</p>'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
expect($html)->toContain('Hero title');
|
||||
expect($html)->toContain('N/A');
|
||||
});
|
||||
7
tests/Feature/ExampleTest.php
Normal file
7
tests/Feature/ExampleTest.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
test('the application returns a successful response', function () {
|
||||
$response = $this->get('/');
|
||||
|
||||
$response->assertStatus(200);
|
||||
});
|
||||
16
tests/Feature/LocalizedRoutesTest.php
Normal file
16
tests/Feature/LocalizedRoutesTest.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use function Pest\Laravel\get;
|
||||
|
||||
it('renders localized frontend links on locale prefixed pages', function () {
|
||||
get('/sl')
|
||||
->assertSuccessful()
|
||||
->assertSee('/sl/contact', false)
|
||||
->assertSee('/sl', false);
|
||||
});
|
||||
|
||||
it('renders the localized terms page without falling through slug routing', function () {
|
||||
get('/sl/terms')
|
||||
->assertSuccessful()
|
||||
->assertSee('Terms', false);
|
||||
});
|
||||
99
tests/Feature/PagesLanguagesTest.php
Normal file
99
tests/Feature/PagesLanguagesTest.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
use cPad\Plugins\Pages\Controllers\PagesController;
|
||||
use cPad\Plugins\Pages\Services\Content;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Klevze\ControlPanel\Facades\ActiveLanguage;
|
||||
use Klevze\ControlPanel\Models\Content\ActiveLanguage as ActiveLanguageModel;
|
||||
use Klevze\ControlPanel\Models\Content\Language;
|
||||
use Klevze\ControlPanel\Services\Config;
|
||||
|
||||
beforeEach(function () {
|
||||
View::addNamespace('plugin.page', base_path('packages/klevze/Plugins/Pages/Resources/views'));
|
||||
|
||||
Schema::dropIfExists('cpad');
|
||||
Schema::dropIfExists('active_languages');
|
||||
Schema::dropIfExists('languages');
|
||||
|
||||
Schema::create('languages', function (Blueprint $table) {
|
||||
$table->char('iso', 2)->primary();
|
||||
$table->string('name', 50);
|
||||
$table->string('native', 50);
|
||||
});
|
||||
|
||||
Schema::create('active_languages', function (Blueprint $table) {
|
||||
$table->id('language_id');
|
||||
$table->char('iso', 2)->unique();
|
||||
$table->boolean('active')->default(false);
|
||||
$table->unsignedTinyInteger('num');
|
||||
$table->char('type', 16);
|
||||
});
|
||||
|
||||
Schema::create('cpad', function (Blueprint $table) {
|
||||
$table->string('keycode')->primary();
|
||||
$table->text('value')->nullable();
|
||||
});
|
||||
|
||||
Language::create(['iso' => 'sl', 'name' => 'Slovenian', 'native' => 'Slovenščina']);
|
||||
Language::create(['iso' => 'en', 'name' => 'English', 'native' => 'English']);
|
||||
|
||||
ActiveLanguageModel::create(['iso' => 'sl', 'active' => true, 'num' => 1, 'type' => 'fp']);
|
||||
ActiveLanguageModel::create(['iso' => 'en', 'active' => true, 'num' => 2, 'type' => 'fp']);
|
||||
|
||||
ActiveLanguage::clearCache();
|
||||
|
||||
\Klevze\ControlPanel\Models\System\Cpad::create([
|
||||
'keycode' => 'plugin.pages.config',
|
||||
'value' => '{}',
|
||||
]);
|
||||
});
|
||||
|
||||
it('loads fp languages for the pages create form', function () {
|
||||
$controller = new class(app(Content::class), app(Config::class)) extends PagesController {
|
||||
public function getFontIcons()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getCategories()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getCategoryTree()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getPagesList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getGalleryList(?int $id)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getTranslation($content_id)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
$view = $controller->Create();
|
||||
$data = $view->getData();
|
||||
|
||||
expect($data['languages'])
|
||||
->toHaveCount(2)
|
||||
->and(collect($data['languages'])->pluck('iso')->all())
|
||||
->toBe(['sl', 'en'])
|
||||
->and($data['translationForm'])
|
||||
->toBeArray()
|
||||
->and($data['translationForm'])
|
||||
->not->toBeEmpty()
|
||||
->and($data['translationForm'][0]->name)
|
||||
->toBe('title');
|
||||
});
|
||||
73
tests/Feature/PagesSetupControllerTest.php
Normal file
73
tests/Feature/PagesSetupControllerTest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
use cPad\Plugins\Pages\Controllers\SetupController;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Klevze\ControlPanel\Models\System\Cpad;
|
||||
use Klevze\ControlPanel\Services\Config;
|
||||
|
||||
beforeEach(function () {
|
||||
View::addNamespace('plugin.page', base_path('packages/klevze/Plugins/Pages/Resources/views'));
|
||||
|
||||
Schema::dropIfExists('cpad');
|
||||
Schema::create('cpad', function (Blueprint $table) {
|
||||
$table->string('keycode')->primary();
|
||||
$table->text('value')->nullable();
|
||||
});
|
||||
});
|
||||
|
||||
it('decodes stored pages setup config before rendering the setup view', function () {
|
||||
Cpad::create([
|
||||
'keycode' => 'plugin.pages.fields',
|
||||
'value' => '[{"type":"text","name":"title","label":"Title","required":true,"store":"title"}]',
|
||||
]);
|
||||
|
||||
Cpad::create([
|
||||
'keycode' => 'plugin.pages.config',
|
||||
'value' => '{"folderDivider":150,"localeLinks":"Y"}',
|
||||
]);
|
||||
|
||||
$view = app(SetupController::class)->Setup(Request::create('/cp/content/pages/setup', 'GET'));
|
||||
|
||||
expect($view->getData()['form'])
|
||||
->toBeArray()
|
||||
->and($view->getData()['form'][0]->name)
|
||||
->toBe('title')
|
||||
->and($view->getData()['setup'])
|
||||
->toBeObject()
|
||||
->and($view->getData()['setup']->folderDivider)
|
||||
->toBe(150);
|
||||
});
|
||||
|
||||
it('flushes and repopulates cached pages settings after saving setup', function () {
|
||||
Cpad::create([
|
||||
'keycode' => 'plugin.pages.fields',
|
||||
'value' => '[{"type":"text","name":"title","label":"Title","required":true,"store":"title"}]',
|
||||
]);
|
||||
|
||||
Cpad::create([
|
||||
'keycode' => 'plugin.pages.config',
|
||||
'value' => '{"folderDivider":100,"localeLinks":"N"}',
|
||||
]);
|
||||
|
||||
$controller = app(SetupController::class);
|
||||
|
||||
$response = $controller->Update(Request::create('/cp/content/pages/setup', 'POST', [
|
||||
'setup' => [
|
||||
'folderDivider' => 200,
|
||||
'localeLinks' => 'Y',
|
||||
],
|
||||
'fields' => '[{"type":"text","name":"title","label":"Updated Title","required":true,"store":"title"}]',
|
||||
]));
|
||||
|
||||
expect($response->getStatusCode())->toBe(302);
|
||||
|
||||
$config = app(Config::class);
|
||||
|
||||
expect($config->checkConfig('plugin.pages.config'))
|
||||
->toBeTrue()
|
||||
->and($config->checkConfig('plugin.pages.fields'))
|
||||
->toBeTrue();
|
||||
});
|
||||
118
tests/Feature/ProjectsEditCategoriesRetentionTest.php
Normal file
118
tests/Feature/ProjectsEditCategoriesRetentionTest.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
use cPad\Plugins\Projects\Controllers\ProductsController;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
beforeEach(function () {
|
||||
Schema::dropIfExists('projects_description');
|
||||
Schema::dropIfExists('projects');
|
||||
|
||||
Schema::create('projects', function (Blueprint $table) {
|
||||
$table->increments('project_id');
|
||||
$table->unsignedTinyInteger('active')->default(1);
|
||||
$table->json('categories')->nullable();
|
||||
$table->text('category_notes')->nullable();
|
||||
$table->text('structure')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('projects_description', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('project_id');
|
||||
$table->char('iso', 3);
|
||||
$table->string('headline')->nullable();
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps existing categories when the settings panel is not submitted', function () {
|
||||
DB::table('projects')->insert([
|
||||
'project_id' => 18,
|
||||
'active' => 1,
|
||||
'categories' => json_encode(['1', '2']),
|
||||
'category_notes' => json_encode(['1' => 'Existing note']),
|
||||
'structure' => null,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('projects_description')->insert([
|
||||
'project_id' => 18,
|
||||
'iso' => 'sl',
|
||||
'headline' => 'Before',
|
||||
]);
|
||||
|
||||
try {
|
||||
app(ProductsController::class)->update(18, Request::create('/cp/projects/edit/18', 'POST', [
|
||||
'active' => '1',
|
||||
'categories_touched' => '0',
|
||||
]));
|
||||
} catch (\Throwable) {
|
||||
// The admin redirect route is not loaded in this isolated feature test.
|
||||
}
|
||||
|
||||
expect(DB::table('projects')->where('project_id', 18)->value('categories'))->toBe(json_encode(['1', '2']));
|
||||
expect(DB::table('projects')->where('project_id', 18)->value('category_notes'))->toBe(json_encode(['1' => 'Existing note']));
|
||||
});
|
||||
|
||||
it('clears categories when the settings panel is submitted without any selected categories', function () {
|
||||
DB::table('projects')->insert([
|
||||
'project_id' => 18,
|
||||
'active' => 1,
|
||||
'categories' => json_encode(['1', '2']),
|
||||
'category_notes' => json_encode(['1' => 'Existing note']),
|
||||
'structure' => null,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('projects_description')->insert([
|
||||
'project_id' => 18,
|
||||
'iso' => 'sl',
|
||||
'headline' => 'Before',
|
||||
]);
|
||||
|
||||
try {
|
||||
app(ProductsController::class)->update(18, Request::create('/cp/projects/edit/18', 'POST', [
|
||||
'active' => '1',
|
||||
'categories_touched' => '1',
|
||||
]));
|
||||
} catch (\Throwable) {
|
||||
// The admin redirect route is not loaded in this isolated feature test.
|
||||
}
|
||||
|
||||
expect(DB::table('projects')->where('project_id', 18)->value('categories'))->toBe(json_encode([]));
|
||||
expect(DB::table('projects')->where('project_id', 18)->value('category_notes'))->toBe(json_encode(['1' => 'Existing note']));
|
||||
});
|
||||
|
||||
it('deduplicates categories when the settings panel submits repeated values', function () {
|
||||
DB::table('projects')->insert([
|
||||
'project_id' => 18,
|
||||
'active' => 1,
|
||||
'categories' => json_encode(['1']),
|
||||
'category_notes' => null,
|
||||
'structure' => null,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('projects_description')->insert([
|
||||
'project_id' => 18,
|
||||
'iso' => 'sl',
|
||||
'headline' => 'Before',
|
||||
]);
|
||||
|
||||
try {
|
||||
app(ProductsController::class)->update(18, Request::create('/cp/projects/edit/18', 'POST', [
|
||||
'active' => '1',
|
||||
'categories_touched' => '1',
|
||||
'categories' => ['4', '4', '2', '2'],
|
||||
]));
|
||||
} catch (\Throwable) {
|
||||
// The admin redirect route is not loaded in this isolated feature test.
|
||||
}
|
||||
|
||||
expect(DB::table('projects')->where('project_id', 18)->value('categories'))->toBe(json_encode(['4', '2']));
|
||||
});
|
||||
297
tests/Feature/PublicProjectsPagesTest.php
Normal file
297
tests/Feature/PublicProjectsPagesTest.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Tests\Fixtures\View\Components\Block as TestBlockComponent;
|
||||
use function Pest\Laravel\get;
|
||||
|
||||
beforeEach(function () {
|
||||
Schema::dropIfExists('blocks');
|
||||
Schema::dropIfExists('projects_gallery');
|
||||
Schema::dropIfExists('projects_description');
|
||||
Schema::dropIfExists('projects');
|
||||
Schema::dropIfExists('projects_categories_description');
|
||||
Schema::dropIfExists('projects_categories');
|
||||
|
||||
Blade::component('block', TestBlockComponent::class);
|
||||
View::addNamespace('plugin.block', base_path('packages/klevze/Plugins/Blocks/Resources/views'));
|
||||
|
||||
Schema::create('projects_categories', function (Blueprint $table) {
|
||||
$table->increments('category_id');
|
||||
$table->unsignedInteger('parent_id')->default(0);
|
||||
$table->unsignedTinyInteger('active')->default(1);
|
||||
$table->integer('num')->default(0);
|
||||
});
|
||||
|
||||
Schema::create('projects_categories_description', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('category_id');
|
||||
$table->char('iso', 3);
|
||||
$table->string('title');
|
||||
});
|
||||
|
||||
Schema::create('projects', function (Blueprint $table) {
|
||||
$table->increments('project_id');
|
||||
$table->unsignedInteger('category_id')->nullable()->default(0);
|
||||
$table->unsignedTinyInteger('active')->default(1);
|
||||
$table->string('picture_cover')->nullable();
|
||||
$table->string('picture_catalog')->nullable();
|
||||
$table->string('picture_catalog_full')->nullable();
|
||||
$table->string('video')->nullable();
|
||||
$table->json('categories')->nullable();
|
||||
$table->text('structure')->nullable();
|
||||
$table->string('bgColor')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('projects_description', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('project_id');
|
||||
$table->char('iso', 3);
|
||||
$table->string('headline');
|
||||
$table->string('subline')->nullable();
|
||||
$table->string('name')->nullable();
|
||||
$table->integer('year')->nullable();
|
||||
$table->text('preview')->nullable();
|
||||
$table->longText('content')->nullable();
|
||||
$table->string('tag1')->nullable();
|
||||
$table->string('tag2')->nullable();
|
||||
$table->string('tag3')->nullable();
|
||||
$table->string('youtube')->nullable();
|
||||
$table->string('page_title')->nullable();
|
||||
$table->string('meta_title')->nullable();
|
||||
$table->string('meta_description')->nullable();
|
||||
$table->string('og_title')->nullable();
|
||||
$table->string('og_description')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('projects_gallery', function (Blueprint $table) {
|
||||
$table->increments('photo_id');
|
||||
$table->unsignedInteger('project_id');
|
||||
$table->integer('num')->default(0);
|
||||
$table->string('diskname');
|
||||
$table->string('name')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('blocks', function (Blueprint $table) {
|
||||
$table->increments('block_id');
|
||||
$table->string('keycode', 64)->unique();
|
||||
$table->json('attributes')->nullable();
|
||||
$table->string('notes')->nullable();
|
||||
$table->boolean('active')->default(true);
|
||||
$table->string('block_group', 16)->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
DB::table('blocks')->insert([
|
||||
[
|
||||
'keycode' => 'WORK-HEADER',
|
||||
'attributes' => json_encode(['sl' => ['content' => '']]),
|
||||
'active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'keycode' => 'FOOTER-BLOCK',
|
||||
'attributes' => json_encode(['sl' => ['content' => '']]),
|
||||
'active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'keycode' => 'FOOTER-HOME-BLOCK',
|
||||
'attributes' => json_encode(['sl' => ['content' => '']]),
|
||||
'active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
function seedPublicProject(int $id, string $headline, int $categoryId, ?array $structure = null): void
|
||||
{
|
||||
DB::table('projects')->insert([
|
||||
'project_id' => $id,
|
||||
'category_id' => $categoryId,
|
||||
'active' => 1,
|
||||
'categories' => json_encode([$categoryId]),
|
||||
'structure' => $structure ? json_encode($structure) : null,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('projects_description')->insert([
|
||||
'project_id' => $id,
|
||||
'iso' => 'sl',
|
||||
'headline' => $headline,
|
||||
'name' => 'Client ' . $id,
|
||||
'preview' => 'Preview ' . $id,
|
||||
'content' => 'Content ' . $id,
|
||||
'tag1' => 'Award ' . $id,
|
||||
'youtube' => 'https://www.youtube.com/watch?v=ScMzIvxBSi4',
|
||||
]);
|
||||
}
|
||||
|
||||
it('renders public work cards from projects records', function () {
|
||||
DB::table('projects_categories')->insert([
|
||||
'category_id' => 1,
|
||||
'parent_id' => 0,
|
||||
'active' => 1,
|
||||
'num' => 1,
|
||||
]);
|
||||
|
||||
DB::table('projects_categories_description')->insert([
|
||||
'category_id' => 1,
|
||||
'iso' => 'sl',
|
||||
'title' => 'Branding',
|
||||
]);
|
||||
|
||||
seedPublicProject(10, 'Golden Drum', 1);
|
||||
|
||||
get('/sl/work')
|
||||
->assertSuccessful()
|
||||
->assertSee('Golden Drum', false)
|
||||
->assertSee('/sl/project/10/golden-drum', false)
|
||||
->assertSee('Branding', false);
|
||||
});
|
||||
|
||||
it('renders bunny video thumbnails on the work page when configured in project settings', function () {
|
||||
DB::table('projects_categories')->insert([
|
||||
'category_id' => 1,
|
||||
'parent_id' => 0,
|
||||
'active' => 1,
|
||||
'num' => 1,
|
||||
]);
|
||||
|
||||
DB::table('projects_categories_description')->insert([
|
||||
'category_id' => 1,
|
||||
'iso' => 'sl',
|
||||
'title' => 'Branding',
|
||||
]);
|
||||
|
||||
seedPublicProject(18, 'Video Thumb Project', 1, [
|
||||
'header' => ['headline' => 'Video Thumb Project'],
|
||||
'thumbnailMedia' => [
|
||||
'type' => 'bunny',
|
||||
'url' => 'https://player.mediadelivery.net/embed/12345/abcde',
|
||||
'autoplay' => true,
|
||||
'loop' => true,
|
||||
'muted' => true,
|
||||
],
|
||||
'heroMedia' => ['type' => 'image', 'url' => ''],
|
||||
'metadata' => ['clientName' => 'Client 18', 'awarded' => [], 'description' => 'Preview 18'],
|
||||
'contentBlocks' => [],
|
||||
]);
|
||||
|
||||
get('/sl/work')
|
||||
->assertSuccessful()
|
||||
->assertSee('Video Thumb Project', false)
|
||||
->assertSee('player.mediadelivery.net/embed/12345/abcde', false)
|
||||
->assertSee('autoplay=true', false)
|
||||
->assertSee('loop=true', false)
|
||||
->assertSee('muted=true', false);
|
||||
});
|
||||
|
||||
it('falls back to the bunny hero video on the work page when the thumbnail is set to bunny without its own url', function () {
|
||||
DB::table('projects_categories')->insert([
|
||||
'category_id' => 1,
|
||||
'parent_id' => 0,
|
||||
'active' => 1,
|
||||
'num' => 1,
|
||||
]);
|
||||
|
||||
DB::table('projects_categories_description')->insert([
|
||||
'category_id' => 1,
|
||||
'iso' => 'sl',
|
||||
'title' => 'Branding',
|
||||
]);
|
||||
|
||||
seedPublicProject(18, 'Haloško srce', 1, [
|
||||
'header' => ['headline' => 'Haloško srce'],
|
||||
'thumbnailMedia' => [
|
||||
'type' => 'bunny',
|
||||
'url' => '',
|
||||
'autoplay' => true,
|
||||
'loop' => true,
|
||||
'muted' => true,
|
||||
],
|
||||
'heroMedia' => [
|
||||
'type' => 'bunny',
|
||||
'url' => 'https://player.mediadelivery.net/embed/98765/hero-video',
|
||||
'autoplay' => true,
|
||||
'loop' => true,
|
||||
'muted' => true,
|
||||
],
|
||||
'metadata' => ['clientName' => 'Client 18', 'awarded' => [], 'description' => 'Preview 18'],
|
||||
'contentBlocks' => [],
|
||||
]);
|
||||
|
||||
get('/sl/work')
|
||||
->assertSuccessful()
|
||||
->assertSee('Haloško srce', false)
|
||||
->assertSee('player.mediadelivery.net/embed/98765/hero-video', false)
|
||||
->assertSee('autoplay=true', false)
|
||||
->assertSee('loop=true', false)
|
||||
->assertSee('muted=true', false);
|
||||
});
|
||||
|
||||
it('renders the selected project and the next project navigation', function () {
|
||||
DB::table('projects_categories')->insert([
|
||||
['category_id' => 1, 'parent_id' => 0, 'active' => 1, 'num' => 1],
|
||||
['category_id' => 2, 'parent_id' => 0, 'active' => 1, 'num' => 2],
|
||||
]);
|
||||
|
||||
DB::table('projects_categories_description')->insert([
|
||||
['category_id' => 1, 'iso' => 'sl', 'title' => 'Branding'],
|
||||
['category_id' => 2, 'iso' => 'sl', 'title' => 'Campaign'],
|
||||
]);
|
||||
|
||||
seedPublicProject(11, 'First Project', 1, [
|
||||
'header' => ['headline' => 'First Project'],
|
||||
'heroMedia' => ['type' => 'youtube', 'url' => 'https://www.youtube.com/watch?v=ScMzIvxBSi4'],
|
||||
'metadata' => ['clientName' => 'SOZ', 'awarded' => ['Award 1'], 'description' => 'Desc'],
|
||||
'contentBlocks' => [],
|
||||
]);
|
||||
seedPublicProject(9, 'Second Project', 2);
|
||||
|
||||
get('/sl/project/11/first-project')
|
||||
->assertSuccessful()
|
||||
->assertSee('First Project', false)
|
||||
->assertSee('/sl/project/9/second-project', false)
|
||||
->assertSee('Next project', false);
|
||||
});
|
||||
|
||||
it('prefers the translated project description over the schema sample', function () {
|
||||
DB::table('projects_categories')->insert([
|
||||
'category_id' => 1,
|
||||
'parent_id' => 0,
|
||||
'active' => 1,
|
||||
'num' => 1,
|
||||
]);
|
||||
|
||||
DB::table('projects_categories_description')->insert([
|
||||
'category_id' => 1,
|
||||
'iso' => 'sl',
|
||||
'title' => 'Branding',
|
||||
]);
|
||||
|
||||
seedPublicProject(12, 'Project Twelve', 1, [
|
||||
'header' => ['headline' => ''],
|
||||
'heroMedia' => ['type' => 'image', 'url' => ''],
|
||||
'metadata' => ['clientName' => 'SOZ', 'awarded' => [], 'description' => 'A modular project schema that can drive both the public page and the cPad editor preview.'],
|
||||
'contentBlocks' => [],
|
||||
]);
|
||||
|
||||
DB::table('projects_description')->where('project_id', 12)->update([
|
||||
'preview' => 'Ne za kogarkoli, ampak za Golden Drum. Festival, ki vstopa v četrto desetletje in še vedno premika meje.',
|
||||
]);
|
||||
|
||||
get('/sl/project/12/project-twelve')
|
||||
->assertSuccessful()
|
||||
->assertSee('Ne za kogarkoli, ampak za Golden Drum. Festival, ki vstopa v četrto desetletje in še vedno premika meje.', false)
|
||||
->assertDontSee('A modular project schema that can drive both the public page and the cPad editor preview.', false);
|
||||
});
|
||||
15
tests/Fixtures/View/Components/Block.php
Normal file
15
tests/Fixtures/View/Components/Block.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Fixtures\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Block extends Component
|
||||
{
|
||||
public function render(): Closure|View|string
|
||||
{
|
||||
return static fn () => '';
|
||||
}
|
||||
}
|
||||
49
tests/Pest.php
Normal file
49
tests/Pest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||
| need to change it using the "pest()" function to bind a different classes or traits.
|
||||
|
|
||||
*/
|
||||
|
||||
pest()->extend(TestCase::class)
|
||||
// ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
|
||||
->in('Feature');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||
|
|
||||
*/
|
||||
|
||||
expect()->extend('toBeOne', function () {
|
||||
return $this->toBe(1);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Functions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
||||
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
||||
| global functions to help you to reduce the number of lines of code in your test files.
|
||||
|
|
||||
*/
|
||||
|
||||
function something()
|
||||
{
|
||||
// ..
|
||||
}
|
||||
10
tests/TestCase.php
Normal file
10
tests/TestCase.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
//
|
||||
}
|
||||
5
tests/Unit/ExampleTest.php
Normal file
5
tests/Unit/ExampleTest.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
test('that true is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
Reference in New Issue
Block a user