225 lines
7.1 KiB
PHP
225 lines
7.1 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit;
|
||
|
||
use PHPUnit\Framework\TestCase;
|
||
use System\Classes\View;
|
||
use System\Classes\MyException;
|
||
|
||
class ViewTest extends TestCase
|
||
{
|
||
private string $tmpFile;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$this->tmpFile = sys_get_temp_dir() . '/bicycle_view_' . uniqid('', true) . '.html';
|
||
}
|
||
|
||
protected function tearDown(): void
|
||
{
|
||
if (file_exists($this->tmpFile)) {
|
||
unlink($this->tmpFile);
|
||
}
|
||
|
||
// Сбрасываем статические реестры CSS/JS/manifest между тестами
|
||
$ref = new \ReflectionClass(View::class);
|
||
foreach (['_styles' => [], '_scripts' => [], '_manifest' => null] as $prop => $default) {
|
||
$property = $ref->getProperty($prop);
|
||
$property->setAccessible(true);
|
||
$property->setValue(null, $default);
|
||
}
|
||
}
|
||
|
||
public function testRenderStaticContent(): void
|
||
{
|
||
file_put_contents($this->tmpFile, 'hello world');
|
||
$view = new View();
|
||
$view->_file = $this->tmpFile;
|
||
$this->assertSame('hello world', $view->render());
|
||
}
|
||
|
||
public function testRenderWithData(): void
|
||
{
|
||
file_put_contents($this->tmpFile, '<?php echo $name; ?>');
|
||
$view = new View();
|
||
$view->_file = $this->tmpFile;
|
||
$view->_data = ['name' => 'bicycle'];
|
||
$this->assertSame('bicycle', $view->render());
|
||
}
|
||
|
||
public function testRenderMultipleVars(): void
|
||
{
|
||
file_put_contents($this->tmpFile, '<?php echo "$a-$b"; ?>');
|
||
$view = new View();
|
||
$view->_file = $this->tmpFile;
|
||
$view->_data = ['a' => 'foo', 'b' => 'bar'];
|
||
$this->assertSame('foo-bar', $view->render());
|
||
}
|
||
|
||
public function testRenderThrowsWhenFileNotSet(): void
|
||
{
|
||
$this->expectException(MyException::class);
|
||
(new View())->render();
|
||
}
|
||
|
||
public function testSetFilenameFindsFileInAppPath(): void
|
||
{
|
||
$view = new View();
|
||
$view->setFilename('index', 'view/Index');
|
||
$this->assertStringContainsString('App', $view->_file);
|
||
$this->assertStringEndsWith('index.html', $view->_file);
|
||
$this->assertFileExists($view->_file);
|
||
}
|
||
|
||
public function testSetFilenameFindsFileInSysPath(): void
|
||
{
|
||
$view = new View();
|
||
$view->setFilename('404', 'view/errors');
|
||
$this->assertStringContainsString('System', $view->_file);
|
||
$this->assertFileExists($view->_file);
|
||
}
|
||
|
||
public function testSetFilenameThrowsWhenNotFound(): void
|
||
{
|
||
$this->expectException(MyException::class);
|
||
(new View())->setFilename('nonexistent_xyz', 'view/Index');
|
||
}
|
||
|
||
public function testSetFilenameThrowsWhenDirEmpty(): void
|
||
{
|
||
$this->expectException(MyException::class);
|
||
(new View())->setFilename('index', '');
|
||
}
|
||
|
||
public function testConstructorWithFileAndDir(): void
|
||
{
|
||
$view = new View('index', 'view/Index');
|
||
$this->assertNotEmpty($view->_file);
|
||
$this->assertFileExists($view->_file);
|
||
}
|
||
|
||
public function testConstructorWithData(): void
|
||
{
|
||
file_put_contents($this->tmpFile, '<?php echo $val; ?>');
|
||
$view = new View();
|
||
$view->_file = $this->tmpFile;
|
||
$view->_data = ['val' => 'test_value'];
|
||
$this->assertSame('test_value', $view->render());
|
||
}
|
||
|
||
public function testExtractSkipDoesNotOverrideLocalVar(): void
|
||
{
|
||
// EXTR_SKIP не перезаписывает уже существующие переменные в шаблоне
|
||
file_put_contents($this->tmpFile, '<?php $x = "local"; extract([], EXTR_SKIP); echo $x; ?>');
|
||
$view = new View();
|
||
$view->_file = $this->tmpFile;
|
||
$view->_data = ['x' => 'from_data'];
|
||
// Здесь x уже определена в шаблоне ДО extract — EXTR_SKIP не перезапишет
|
||
// Но View делает extract ДО include, поэтому $x = 'from_data' попадёт в шаблон
|
||
$this->assertSame('local', $view->render());
|
||
}
|
||
|
||
public function testSetFilenameReturnsSelf(): void
|
||
{
|
||
$view = new View();
|
||
$result = $view->setFilename('index', 'view/Index');
|
||
$this->assertSame($view, $result);
|
||
}
|
||
|
||
public function testSetStyleRegistersLinkTag(): void
|
||
{
|
||
(new View())->setStyle('app.css');
|
||
ob_start();
|
||
(new View())->getStyles();
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('<link rel="stylesheet" href="/App/media/css/app.css">', $html);
|
||
}
|
||
|
||
public function testSetStyleMissingFileEchoesAlert(): void
|
||
{
|
||
ob_start();
|
||
(new View())->setStyle('nonexistent_xyz.css');
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('alert-danger', $html);
|
||
$this->assertStringContainsString('nonexistent_xyz.css', $html);
|
||
|
||
ob_start();
|
||
(new View())->getStyles();
|
||
$this->assertSame('', ob_get_clean());
|
||
}
|
||
|
||
public function testSetScriptRegistersScriptTag(): void
|
||
{
|
||
(new View())->setScript('app.js');
|
||
ob_start();
|
||
(new View())->getScripts();
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString(
|
||
'<script type="text/javascript" src="/App/media/js/app.js"></script>', $html
|
||
);
|
||
}
|
||
|
||
public function testSetScriptCustomType(): void
|
||
{
|
||
(new View())->setScript('app.js', 'module');
|
||
ob_start();
|
||
(new View())->getScripts();
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('type="module"', $html);
|
||
}
|
||
|
||
public function testSetScriptMissingFileEchoesAlert(): void
|
||
{
|
||
ob_start();
|
||
(new View())->setScript('nonexistent_xyz.js');
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('alert-danger', $html);
|
||
|
||
ob_start();
|
||
(new View())->getScripts();
|
||
$this->assertSame('', ob_get_clean());
|
||
}
|
||
|
||
public function testSetManifestRegistersLinkTag(): void
|
||
{
|
||
(new View())->setManifest('manifest.json');
|
||
ob_start();
|
||
(new View())->getStyles();
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('<link rel="manifest" href="/App/media/manifest.json">', $html);
|
||
}
|
||
|
||
public function testSetManifestMissingFileEchoesAlert(): void
|
||
{
|
||
ob_start();
|
||
(new View())->setManifest('nonexistent_xyz.json');
|
||
$html = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('alert-danger', $html);
|
||
}
|
||
|
||
public function testGetStylesAndScriptsAreSharedAcrossViewInstances(): void
|
||
{
|
||
// content и layout — разные объекты View, но реестр общий (см. Controller::render())
|
||
(new View())->setStyle('app.css');
|
||
(new View())->setScript('app.js');
|
||
|
||
ob_start();
|
||
(new View())->getStyles();
|
||
$styles = ob_get_clean();
|
||
|
||
ob_start();
|
||
(new View())->getScripts();
|
||
$scripts = ob_get_clean();
|
||
|
||
$this->assertStringContainsString('app.css', $styles);
|
||
$this->assertStringContainsString('app.js', $scripts);
|
||
}
|
||
}
|