fix(console): comprehensive TUI rendering fixes
- Fix Enter key detection: handle multiple Enter key formats (\n, \r, \r\n) - Reduce flickering: lower render frequency from 60 FPS to 30 FPS - Fix menu bar visibility: re-render menu bar after content to prevent overwriting - Fix content positioning: explicit line positioning for categories and commands - Fix line shifting: clear lines before writing, control newlines manually - Limit visible items: prevent overflow with maxVisibleCategories/Commands - Improve CPU usage: increase sleep interval when no events processed This fixes: - Enter key not working for selection - Strong flickering of the application - Menu bar not visible or being overwritten - Top half of selection list not displayed - Lines being shifted/misaligned
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Http\HttpRequest;
|
||||
use App\Framework\Http\Method;
|
||||
use App\Framework\Http\RequestId;
|
||||
use App\Framework\Meta\MetaData;
|
||||
use App\Framework\Router\Result\ViewResult;
|
||||
use App\Framework\Router\RouteResponder;
|
||||
use App\Framework\View\RenderContext;
|
||||
use App\Framework\View\TemplateRenderer;
|
||||
|
||||
/**
|
||||
* Integration test for RouteResponder admin layout functionality
|
||||
*
|
||||
* Tests the automatic admin layout enrichment for admin routes.
|
||||
* Uses real instances since many framework classes are final.
|
||||
*/
|
||||
describe('RouteResponder - Admin Layout Integration', function () {
|
||||
beforeEach(function () {
|
||||
$this->pathProvider = new PathProvider(__DIR__ . '/../../../../');
|
||||
$this->container = new DefaultContainer();
|
||||
$this->templateRenderer = Mockery::mock(TemplateRenderer::class);
|
||||
$this->requestId = new RequestId('test-secret-for-integration-tests');
|
||||
});
|
||||
|
||||
it('detects admin routes correctly using reflection', function () {
|
||||
$request = new HttpRequest(
|
||||
method: Method::GET,
|
||||
path: '/admin/dashboard',
|
||||
id: $this->requestId
|
||||
);
|
||||
|
||||
$responder = new RouteResponder(
|
||||
$this->pathProvider,
|
||||
$this->container,
|
||||
$this->templateRenderer,
|
||||
$request
|
||||
);
|
||||
|
||||
// Use reflection to test private method
|
||||
$reflection = new ReflectionClass($responder);
|
||||
$method = $reflection->getMethod('isAdminRoute');
|
||||
$method->setAccessible(true);
|
||||
|
||||
expect($method->invoke($responder, '/admin/dashboard'))->toBeTrue();
|
||||
expect($method->invoke($responder, '/admin'))->toBeTrue();
|
||||
expect($method->invoke($responder, '/admin/users'))->toBeTrue();
|
||||
expect($method->invoke($responder, '/'))->toBeFalse();
|
||||
expect($method->invoke($responder, '/api/users'))->toBeFalse();
|
||||
expect($method->invoke($responder, '/admin-api/users'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not enrich non-admin routes', function () {
|
||||
$request = new HttpRequest(
|
||||
method: Method::GET,
|
||||
path: '/home',
|
||||
id: $this->requestId
|
||||
);
|
||||
|
||||
$responder = new RouteResponder(
|
||||
$this->pathProvider,
|
||||
$this->container,
|
||||
$this->templateRenderer,
|
||||
$request
|
||||
);
|
||||
|
||||
$viewResult = new ViewResult(
|
||||
template: 'home',
|
||||
metaData: new MetaData('Home'),
|
||||
data: ['title' => 'Home']
|
||||
);
|
||||
|
||||
$context = $responder->getContext($viewResult);
|
||||
|
||||
expect($context)->toBeInstanceOf(RenderContext::class);
|
||||
expect($context->data)->not->toHaveKey('navigation_menu');
|
||||
expect($context->data)->not->toHaveKey('breadcrumbs');
|
||||
expect($context->data['title'])->toBe('Home');
|
||||
});
|
||||
|
||||
it('handles missing AdminLayoutProcessor gracefully for admin routes', function () {
|
||||
$request = new HttpRequest(
|
||||
method: Method::GET,
|
||||
path: '/admin/dashboard',
|
||||
id: $this->requestId
|
||||
);
|
||||
|
||||
// Container doesn't have AdminLayoutProcessor registered
|
||||
// (it's a fresh container without it)
|
||||
|
||||
$responder = new RouteResponder(
|
||||
$this->pathProvider,
|
||||
$this->container,
|
||||
$this->templateRenderer,
|
||||
$request
|
||||
);
|
||||
|
||||
$viewResult = new ViewResult(
|
||||
template: 'dashboard',
|
||||
metaData: new MetaData('Dashboard'),
|
||||
data: ['title' => 'Dashboard']
|
||||
);
|
||||
|
||||
// Should not throw, but return original data
|
||||
$context = $responder->getContext($viewResult);
|
||||
|
||||
expect($context)->toBeInstanceOf(RenderContext::class);
|
||||
expect($context->data['title'])->toBe('Dashboard');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user