sfc-8.x-1.3/tests/src/Unit/ComponentBaseTest.php
tests/src/Unit/ComponentBaseTest.php
<?php
namespace Drupal\Tests\sfc\Unit;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Form\FormState;
use Drupal\sfc\ComponentBase;
use Drupal\Tests\UnitTestCase;
use org\bovigo\vfs\vfsStream;
/**
* Tests methods provided by the base component.
*
* @coversDefaultClass \Drupal\sfc\ComponentBase
*
* @group sfc
*/
class ComponentBaseTest extends UnitTestCase {
/**
* Tests the ::getTemplate method.
*/
public function testGetTemplate() {
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
// Test that the template automatically adds functions.
$component->setData('TEMPLATE', 'Hello {{ name }}!');
$this->assertStringContainsString("{{ sfc_prepare_context('test_component') }}", $component->getTemplate());
$this->assertStringContainsString("{{ attach_library('sfc/component.test_component') }}", $component->getTemplate());
$this->assertStringContainsString('Hello {{ name }}!', $component->getTemplate());
$this->assertStringNotContainsString('<!-- SFC debug -->', $component->getTemplate());
// Test that debug markup is present.
$component = new TestComponent([], 'test_component', [], TRUE, 'vfs:/', $file_system, $file_url_generator);
$component->setData('TEMPLATE', 'Hello {{ name }}!');
$this->assertStringContainsString('<!-- SFC debug -->', $component->getTemplate());
}
/**
* Tests the ::getLibrary method.
*/
public function testGetLibrary() {
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$file_url_generator->method('generateString')->willReturnCallback(function ($uri) {
return $uri;
});
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
$this->assertEquals([], $component->getLibrary());
// Test that DEPENDENCIES and LIBRARY are properly merged.
$component->setData('LIBRARY', ['dependencies' => ['foo', 'bar']]);
$this->assertEquals(['dependencies' => ['foo', 'bar']], $component->getLibrary());
$component->setData('DEPENDENCIES', ['foo', 'baz']);
$this->assertEquals(['dependencies' => ['foo', 'bar', 'baz']], $component->getLibrary());
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
$this->assertEquals([], $component->getLibrary());
// Test that DEPENDENCIES alone works as expected.
$component->setData('DEPENDENCIES', ['foo', 'baz']);
$this->assertEquals(['dependencies' => ['foo', 'baz']], $component->getLibrary());
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
$component->setData('CSS', 'foo');
$this->assertEquals([
'css' => [
'theme' => [
'/vfs://sfc/components/test_component/test_component.css' => [],
],
],
], $component->getLibrary());
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
$component->setData('JS', 'foo');
$this->assertEquals([
'js' => [
'/vfs://sfc/components/test_component/test_component.js' => [],
],
], $component->getLibrary());
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
// Tests that when attachments are used, dependencies are added.
$component->setData('ATTACH', 'foo');
$this->assertEquals([
'js' => [
'/vfs://sfc/components/test_component/test_component.js' => [],
],
'dependencies' => [
'core/drupal',
'core/drupalSettings',
'core/jquery',
'core/once',
],
], $component->getLibrary());
$component->setData('VANILLA_JS', TRUE);
$this->assertEquals([
'js' => [
'/vfs://sfc/components/test_component/test_component.js' => [],
],
'dependencies' => [
'core/drupal',
'core/drupalSettings',
'core/once',
],
], $component->getLibrary());
}
/**
* Tests the ::writeAssets method.
*/
public function testWriteAssets() {
vfsStream::setup('sfc');
$js_file = 'vfs://sfc/components/test_component/test_component.js';
$css_file = 'vfs://sfc/components/test_component/test_component.css';
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
// We need these methods to be called to get full test coverage.
$file_system->method('mkdir')->willReturnCallback(function () {
if (!is_dir('vfs://sfc/components/test_component')) {
mkdir('vfs://sfc/components/test_component', 0444, TRUE);
}
return;
});
$file_system->method('chmod')->willReturnCallback(function () {
chmod('vfs://sfc/components', 0777);
chmod('vfs://sfc/components/test_component', 0777);
return;
});
$component = new TestComponent([], 'test_component', [
'provider' => 'sfc',
], FALSE, 'vfs:/', $file_system, $file_url_generator);
$component->writeAssets();
$this->assertFileDoesNotExist($js_file);
$this->assertFileDoesNotExist($css_file);
$component->setData('CSS', '.foo { color: pink; } .bar { background-image: url(../foo); } .baz { background-image: url(/foo); } .quz { background-image: url(http://google.com); .bar { background-image: url(foo); }');
$component->writeAssets();
$this->assertFileExists($css_file);
$this->assertStringEqualsFile($css_file, '.foo { color: pink; } .bar { background-image: url(' . realpath(__DIR__ . '/../../..') . '/../foo); } .baz { background-image: url(/foo); } .quz { background-image: url(http://google.com); .bar { background-image: url(' . realpath(__DIR__ . '/../../..') . '/foo); }');
$component->setData('JS', 'bar');
$component->writeAssets();
$this->assertFileExists($js_file);
$this->assertStringEqualsFile($js_file, "bar\n");
$component->setData('ATTACH', 'baz');
$component->setData('SELECTOR', '.selector');
$component->setData('JS', NULL);
$component->writeAssets();
$this->assertFileExists($js_file);
$contents = file_get_contents($js_file);
$this->assertStringContainsString('baz', $contents);
// Lots of boilerplate JS is added when attachments are used.
$this->assertStringContainsString('Drupal.behaviors.sfc_test_component = {', $contents);
$this->assertStringContainsString('$(".selector", context)', $contents);
$this->assertStringContainsString('attach: function', $contents);
$this->assertStringNotContainsString("once('sfcDetach').each", $contents);
$component->setData('DETACH', 'qux');
$component->writeAssets();
$contents = file_get_contents($js_file);
$this->assertStringContainsString('qux', $contents);
$this->assertStringContainsString('attach: function', $contents);
$this->assertStringContainsString("once.remove('sfcAttach', element)", $contents);
// Test vanilla JS.
$component->setData('VANILLA_JS', TRUE);
$component->writeAssets();
$this->assertFileExists($js_file);
$contents = file_get_contents($js_file);
$this->assertStringContainsString('Drupal.behaviors.sfc_test_component = {', $contents);
$this->assertStringContainsString('attach: function', $contents);
$this->assertStringContainsString('detach: function', $contents);
$this->assertStringContainsString('once(\'sfcAttach\', ".selector", context)', $contents);
$this->assertStringContainsString('once(\'sfcDetach\', ".selector", context)', $contents);
}
/**
* Tests the ::shouldWriteAssets method.
*/
public function testShouldWriteAssets() {
vfsStream::setup('sfc');
$js_file = 'vfs://sfc/components/test_component/test_component.js';
$css_file = 'vfs://sfc/components/test_component/test_component.css';
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$component = new WriteTestConstComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
mkdir('vfs://sfc/components/test_component', 0777, TRUE);
// If the file doesn't exist.
$this->assertTrue($component->shouldWriteAssets());
// If only one of the asset files exists.
touch($css_file);
$this->assertTrue($component->shouldWriteAssets());
// If both asset files exist.
touch($js_file);
$this->assertFalse($component->shouldWriteAssets());
// If one asset file is out of date.
unlink($js_file);
unlink($css_file);
touch($js_file);
touch($css_file, 1);
$this->assertTrue($component->shouldWriteAssets());
// If the other asset file is out of date.
unlink($js_file);
unlink($css_file);
touch($js_file, 1);
touch($css_file);
$this->assertTrue($component->shouldWriteAssets());
// If no assets are defined.
$component = new EmptyTestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
$this->assertFalse($component->shouldWriteAssets());
}
/**
* Tests the ::buildContextForm method.
*/
public function testBuildContextForm() {
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$component = new TestComponent([], 'test_component', [], FALSE, 'vfs:/', $file_system, $file_url_generator);
$this->assertEquals(['foo'], $component->buildContextForm(['foo'], new FormState()));
}
/**
* Ensures that defining constants works as expected.
*/
public function testConstants() {
vfsStream::setup('sfc');
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$file_url_generator->method('generateString')->willReturnCallback(function ($uri) {
return $uri;
});
$component = new TestConstComponent([], 'test_component', [
'provider' => 'sfc',
], FALSE, 'vfs:/', $file_system, $file_url_generator);
$this->assertStringContainsString('template', $component->getTemplate());
$this->assertEquals([
'js' => [
'/public://sfc/components/test_component/test_component.js' => [],
realpath(__DIR__ . '/../../..') . '/relative.js' => [],
],
'css' => [
'theme' => [
'/public://sfc/components/test_component/test_component.css' => [],
'/css.css' => [],
realpath(__DIR__ . '/../../..') . '/relative.css' => [],
],
],
'dependencies' => [
'dependency',
'core/drupal',
'core/drupalSettings',
'core/jquery',
'core/once',
],
], $component->getLibrary());
mkdir('vfs://sfc/components/test_component', 0777, TRUE);
$file_system = $this->createMock(FileSystemInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$component = new WriteTestConstComponent([], 'test_component', [
'provider' => 'sfc',
], FALSE, 'vfs:/', $file_system, $file_url_generator);
$component->writeAssets();
$this->assertFileExists('vfs://sfc/components/test_component/test_component.js');
$this->assertFileExists('vfs://sfc/components/test_component/test_component.css');
}
/**
* {@inheritdoc}
*
* @codeCoverageIgnore
*/
protected function tearDown(): void {
vfsStream::setup('sfc');
if (is_file('vfs://sfc/components/test_component/test_component.js')) {
unlink('vfs://sfc/components/test_component/test_component.js');
}
if (is_file('vfs://sfc/components/test_component/test_component.css')) {
unlink('vfs://sfc/components/test_component/test_component.css');
}
if (is_dir('vfs://sfc/components/test_component')) {
rmdir('vfs://sfc/components/test_component');
rmdir('vfs://sfc/components');
}
}
}
// phpcs:disable
// @codeCoverageIgnoreStart
class EmptyTestComponent extends ComponentBase {}
class TestConstComponent extends ComponentBase {
const TEMPLATE = 'template';
const CSS = 'css';
const JS = 'js';
const SELECTOR = 'selector';
const ATTACH = 'attach';
const DETACH = 'detach';
const DEPENDENCIES = [
'dependency',
];
const LIBRARY = [
'css' => [
'theme' => [
'/css.css' => [],
'relative.css' => [],
],
],
'js' => [
'relative.js' => [],
],
];
}
class WriteTestConstComponent extends TestConstComponent {
protected function getAssetPath() {
$path = parent::getAssetPath();
return str_replace('public://', 'vfs://', $path);
}
}
class TestComponent extends ComponentBase {
protected $data = [];
public function setData($name, $data) {
$this->data[$name] = $data;
}
public function getData($name) {
return isset($this->data[$name]) ? $this->data[$name] : NULL;
}
protected function getAssetPath() {
$path = parent::getAssetPath();
return str_replace('public://', 'vfs://', $path);
}
protected function getAttachmentData() {
return [
'selector' => $this->getData('SELECTOR'),
'attach' => $this->getData('ATTACH'),
'detach' => $this->getData('DETACH'),
'vanilla_js' => $this->getData('VANILLA_JS')
];
}
protected function getCss() {
return $this->replaceCssPaths($this->getData('CSS'));
}
protected function getJs() {
return $this->getData('JS');
}
protected function getTemplateData() {
return $this->getData('TEMPLATE');
}
protected function getLibraryData() {
return $this->getData('LIBRARY');
}
protected function getDependencies() {
return $this->getData('DEPENDENCIES');
}
protected function hasAttachments() {
return $this->getData('ATTACH') || $this->getData('DETACH');
}
protected function hasJs() {
return (bool) $this->getData('JS');
}
protected function hasCss() {
return (bool) $this->getData('CSS');
}
protected function hasLibraryData() {
return (bool) $this->getData('LIBRARY');
}
protected function hasDependencies() {
return (bool) $this->getData('DEPENDENCIES');
}
}
namespace Drupal\sfc;
if (!function_exists('Drupal\sfc\file_url_transform_relative')) {
function file_url_transform_relative($file_url) {
return $file_url;
}
}
if (!function_exists('Drupal\sfc\file_create_url')) {
function file_create_url($uri) {
return $uri;
}
}
if (!function_exists('base_path')) {
function base_path() {
return '/';
}
}
// phpcs:enable
// @codeCoverageIgnoreEnd
