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, '');
$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, '');
$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, '');
$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, '');
$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('', $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(
'', $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('', $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);
}
}