Compare commits

...

26 Commits
3.x ... 8.x-2.x

Author SHA1 Message Date
Jeroen Tubex eff02c9d31 Issue #3279991 by JeroenT, Chi: drupal_entity_form add support for content moderation 2 years ago
George Potter 4803620832 Issue #3222666 by gpotter, theRuslan, Chi, smustgrave, superbiche, letrollpoilu: drupal_menu() incorrectly caches the active parent menu item in two-leveled menu 2 years ago
Chi 68bb4e5505 Fix tests 2 years ago
Rolando Payán Mosqueda b39fcea741 3108210-typeerror-on-drupalblock 2 years ago
kristiaanvandeneynde f51df921a0 Issue #3245953 by kristiaanvandeneynde: TwigExtension::drupalTitle() should check for NullRouteMatch 3 years ago
piggito 2206ea3fae Issue #3219625 by piggito: Possible Variable Overwrite 3 years ago
seanpenn079 03da5f2bdf Issue #3188441 by seanB, Wilfred Waltman: Allow arguments to be passed to token_replace 3 years ago
Chi 0b77db785f Fix PHP 7.2 compatibility 4 years ago
Chi b388508f13 Fixed code style issues 4 years ago
Chi 3897d7de66 Issue #3176561 by beram: Add a test for block plugin cache metadata 4 years ago
beram b8ea21be16 Issue #3176561 by beram: drupal_block does not use the plugin cache metadata 4 years ago
Chi b95167bc57 #3185016 Fix typo 4 years ago
hanoii 0f9c68957e Issue #3185016 by hanoii: Translation filter 4 years ago
Niklan 15103a67e3 Issue #3174903 by Niklan, ygoex: Error: Call to a member function getRegion() on null in hook_theme_suggestions_block_alter() 4 years ago
gchauhan ccf146ac6a Issue #3161956 by tripox, gchauhan: No ID in render array for blocks 4 years ago
CharlieChXNegyesi 05c880e09e Issue #3173946 by Charlie ChX Negyesi, larowlan: drupal_region() overcaches empty 4 years ago
Chi ad8c396b7a Remove usage of deprecated in Twig 2 classes 4 years ago
Chi cf7ec3f5d7 Issue #3087368: Add format_size Twig filter 4 years ago
Chi 21b6079272 Issue #3135932 by mxwright: Twig variable in PHP filter 4 years ago
mdupont 0e9a422bec Issue #3095714 by mdupont, Chi: Add file_uri filter 4 years ago
Chi 84c486c2ed Issue #2994996 by aangel: Field access check should test using language 4 years ago
NesleeCanilPinto 2fac0ae51d Issue #3124739 by Neslee Canil Pinto: Typo error in codebase 4 years ago
Chi 8748966d52 Fix code style issues 5 years ago
Chi 10dca235d8 Specify default theme in browser test 5 years ago
Chi 495f34f162 Improve documentation 5 years ago
Chi f1b0efd44d Issue #3116410 by larowlan: The module may not bubble access cacheability metadata 5 years ago
  1. 33
      composer.json
  2. 16
      phpcs.xml
  3. 899
      src/TwigExtension.php
  4. 48
      tests/src/Functional/TwigTweakTest.php
  5. 569
      tests/src/Kernel/AccessTest.php
  6. 8
      tests/twig_tweak_test/config/install/block.block.claro_powered_by_drupal.yml
  7. 17
      tests/twig_tweak_test/config/install/block.block.classy_page_title.yml
  8. 19
      tests/twig_tweak_test/config/install/block.block.classy_status_messages.yml
  9. 51
      tests/twig_tweak_test/src/Plugin/Block/FooBlock.php
  10. 12
      tests/twig_tweak_test/templates/twig-tweak-test.html.twig
  11. 18
      tests/twig_tweak_test/twig_tweak_test.module

33
composer.json

@ -1,18 +1,19 @@
{
"name": "drupal/twig_tweak",
"type": "drupal-module",
"description": "A Twig extension with some useful functions and filters for Drupal development.",
"keywords": ["Drupal", "Twig"],
"license": "GPL-2.0+",
"homepage": "https://www.drupal.org/project/twig_tweak",
"support": {
"issues": "https://www.drupal.org/project/issues/twig_tweak",
"source": "https://git.drupalcode.org/project/twig_tweak"
},
"require": {
"drupal/core": "^8.7 || ^9.0"
},
"suggest": {
"symfony/var-dumper": "Better dump() function for debugging Twig variables"
}
"name": "drupal/twig_tweak",
"type": "drupal-module",
"description": "A Twig extension with some useful functions and filters for Drupal development.",
"keywords": ["Drupal", "Twig"],
"license": "GPL-2.0+",
"homepage": "https://www.drupal.org/project/twig_tweak",
"support": {
"issues": "https://www.drupal.org/project/issues/twig_tweak",
"source": "https://git.drupalcode.org/project/twig_tweak"
},
"require": {
"drupal/core": "^8.7 || ^9.0",
"twig/twig": "^1.41 || ^2.12"
},
"suggest": {
"symfony/var-dumper": "Better dump() function for debugging Twig variables"
}
}

16
phpcs.xml

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<ruleset name="Twig Tweak">
<description>PHP CodeSniffer configuration for Twig Tweak module.</description>
<arg name="extensions" value="php, module, yml"/>
<rule ref="Drupal"/>
<rule ref="DrupalPractice">
<!-- Dependencies are not injected for performance reason. -->
<exclude name="DrupalPractice.Objects.GlobalDrupal.GlobalDrupal"/>
<!-- False positives. -->
<exclude name="Drupal.Commenting.InlineComment.Empty"/>
<!-- The module does not provide change records. -->
<exclude name="Drupal.Semantics.FunctionTriggerError.TriggerErrorTextLayoutRelaxed"/>
<!-- Code examples have rather long lines. -->
<exclude name="Drupal.Files.LineLength.TooLong"/>
</rule>
</ruleset>

899
src/TwigExtension.php

File diff suppressed because it is too large Load Diff

48
tests/src/Functional/TwigTweakTest.php

@ -22,6 +22,11 @@ class TwigTweakTest extends BrowserTestBase {
use TestFileCreationTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'claro';
/**
* {@inheritdoc}
*/
@ -128,21 +133,20 @@ class TwigTweakTest extends BrowserTestBase {
// -- Test block.
$xpath = '//div[@class = "tt-block"]';
$xpath .= '/img[contains(@src, "/core/themes/classy/logo.svg") and @alt="Home"]';
$xpath .= '/img[contains(@src, "/core/themes/claro/logo.svg") and @alt="Home"]';
$this->assertByXpath($xpath);
// -- Test block with wrapper.
$xpath = '//div[@class = "tt-block-with-wrapper"]';
$xpath .= '/div[@class = "block block-system block-system-branding-block"]';
$xpath .= '/h2[text() = "Branding"]';
$xpath .= '/following-sibling::a[img[contains(@src, "/core/themes/classy/logo.svg") and @alt="Home"]]';
$xpath .= '/following-sibling::a[img[contains(@src, "/core/themes/claro/logo.svg") and @alt="Home"]]';
$xpath .= '/following-sibling::div[@class = "site-name"]/a';
$this->assertByXpath($xpath);
// -- Test region.
$xpath = '//div[@class = "tt-region"]/div[@class = "region region-sidebar-first"]';
$xpath .= '/div[contains(@class, "block-page-title-block") and h1[@class="page-title" and text() = "Log in"]]';
$xpath .= '/following-sibling::div[contains(@class, "block-system-powered-by-block")]/span[. = "Powered by Drupal"]';
$xpath = '//div[@class = "tt-region"]/div[@class = "region region-highlighted"]';
$xpath .= '/div[contains(@class, "block-system-powered-by-block")]';
$this->assertByXpath($xpath);
// -- Test entity default view mode.
@ -176,19 +180,21 @@ class TwigTweakTest extends BrowserTestBase {
// Grant require permissions and test the forms again.
$permissions = ['create page content', 'edit any page content'];
$this->grantPermissions(Role::load(Role::ANONYMOUS_ID), $permissions);
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(Role::ANONYMOUS_ID);
$this->grantPermissions($role, $permissions);
$this->drupalGet('/node/2');
// -- Test entity add form.
$xpath = '//div[@class = "tt-entity-add-form"]/form';
$xpath .= '//input[@name = "title[0][value]" and @value = ""]';
$xpath .= '/../../../div/input[@type = "submit" and @value = "Save"]';
$xpath .= '/../../../../..//div/input[@type = "submit" and @value = "Save"]';
$this->assertByXpath($xpath);
// -- Test entity edit form.
$xpath = '//div[@class = "tt-entity-edit-form"]/form';
$xpath .= '//input[@name = "title[0][value]" and @value = "Alpha"]';
$xpath .= '/../../../div/input[@type = "submit" and @value = "Save"]';
$xpath .= '/../../../../..//div/input[@type = "submit" and @value = "Save"]';
$this->assertByXpath($xpath);
// -- Test field.
@ -290,7 +296,9 @@ class TwigTweakTest extends BrowserTestBase {
$xpath = '//div[@class="tt-contextual-links" and not(div[@data-contextual-id])]';
$this->assertByXpath($xpath);
$this->grantPermissions(Role::load(Role::ANONYMOUS_ID), ['access contextual links']);
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(Role::ANONYMOUS_ID);
$this->grantPermissions($role, ['access contextual links']);
$this->drupalGet($this->getUrl());
$xpath = '//div[@class="tt-contextual-links" and div[@data-contextual-id]]';
$this->assertByXpath($xpath);
@ -311,6 +319,10 @@ class TwigTweakTest extends BrowserTestBase {
$xpath = '//div[@class = "tt-check-markup"]';
self::assertEquals('<b>bold</b> strong', trim($this->xpath($xpath)[0]->getHtml()));
// -- Format size.
$xpath = '//div[@class = "tt-format-size"]';
self::assertSame('12.06 KB', $this->xpath($xpath)[0]->getHtml());
// -- Test truncation.
$xpath = '//div[@class = "tt-truncate" and text() = "Hello…"]';
$this->assertByXpath($xpath);
@ -340,12 +352,28 @@ class TwigTweakTest extends BrowserTestBase {
$xpath = '//div[@class = "tt-field-item-view" and text() = "Beta"]';
$this->assertByXpath($xpath);
// -- Test file URI from image field.
$this->drupalGet('/node/1');
$xpath = '//div[@class = "tt-file-uri-from-image-field" and contains(text(), "public://image-test.png")]';
$this->assertByXpath($xpath);
// -- Test file URI from a specific image field item.
$xpath = '//div[@class = "tt-file-uri-from-image-field-delta" and contains(text(), "public://image-test.png")]';
$this->assertByXpath($xpath);
// -- Test file URI from media field.
$xpath = '//div[@class = "tt-file-uri-from-media-field" and contains(text(), "public://image-1.png")]';
$this->assertByXpath($xpath);
// -- Test image style from file URI from media field.
$xpath = '//div[@class = "tt-image-style-from-file-uri-from-media-field" and contains(text(), "styles/thumbnail/public/image-1.png")]';
$this->assertByXpath($xpath);
// -- Test file URL from URI.
$xpath = '//div[@class = "tt-file-url-from-uri" and contains(text(), "/files/image-test.png")]';
$this->assertByXpath($xpath);
// -- Test file URL from image field.
$this->drupalGet('/node/1');
$xpath = '//div[@class = "tt-file-url-from-image-field" and contains(text(), "/files/image-test.png")]';
$this->assertByXpath($xpath);

569
tests/src/Kernel/AccessTest.php

@ -0,0 +1,569 @@
<?php
namespace Drupal\Tests\twig_tweak\Kernel;
use Drupal\block\BlockViewBuilder;
use Drupal\block\Entity\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\file\Entity\File;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeInterface;
use Drupal\Tests\user\Traits\UserCreationTrait;
/**
* Tests for the Twig Tweak access control.
*
* @group twig_tweak
*/
class AccessTest extends KernelTestBase {
use UserCreationTrait;
/**
* A node for testing.
*
* @var \Drupal\node\NodeInterface
*/
private $node;
/**
* The Twig extension.
*
* @var \Drupal\twig_tweak\TwigExtension
*/
private $twigExtension;
/**
* {@inheritdoc}
*/
protected static $modules = [
'twig_tweak',
'twig_tweak_test',
'node',
'file',
'user',
'system',
'block',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installConfig(['system']);
$node_type = NodeType::create([
'type' => 'article',
'name' => 'Article',
]);
$node_type->save();
$values = [
'type' => 'article',
'status' => NodeInterface::PUBLISHED,
// @see twig_tweak_test_node_access()
'title' => 'Entity access test',
];
$this->node = Node::create($values);
$this->node->save();
$this->twigExtension = $this->container->get('twig_tweak.twig_extension');
}
/**
* Test callback.
*/
public function testDrupalEntity() {
// -- Unprivileged user with access check.
$this->setUpCurrentUser(['name' => 'User 1']);
$build = $this->twigExtension->drupalEntity('node', $this->node->id());
self::assertNull($build);
// -- Unprivileged user without access check.
$build = $this->twigExtension->drupalEntity('node', $this->node->id(), NULL, NULL, FALSE);
self::assertArrayHasKey('#node', $build);
$expected_cache = [
'tags' => [
'node_view',
'node:1',
],
'contexts' => [],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user with access check.
$this->setUpCurrentUser(['name' => 'User 2'], ['access content']);
$build = $this->twigExtension->drupalEntity('node', $this->node->id());
self::assertArrayHasKey('#node', $build);
$expected_cache = [
'tags' => [
'node_view',
'node:1',
'tag_from_twig_tweak_test_node_access',
],
'contexts' => [
'user',
'user.permissions',
],
'max-age' => 50,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user without access check.
$build = $this->twigExtension->drupalEntity('node', $this->node->id(), NULL, NULL, FALSE);
self::assertArrayHasKey('#node', $build);
$expected_cache = [
'tags' => [
'node_view',
'node:1',
],
'contexts' => [],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
}
/**
* Test callback.
*/
public function testDrupalField() {
// -- Unprivileged user with access check.
$this->setUpCurrentUser(['name' => 'User 1']);
$build = $this->twigExtension->drupalField('title', 'node', $this->node->id());
self::assertNull($build);
// -- Unprivileged user without access check.
$build = $this->twigExtension->drupalField('title', 'node', $this->node->id(), 'default', NULL, FALSE);
self::assertArrayHasKey('#items', $build);
$expected_cache = [
'contexts' => [],
'tags' => ['node:1'],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user with access check.
$this->setUpCurrentUser(['name' => 'User 2'], ['access content']);
$build = $this->twigExtension->drupalField('title', 'node', $this->node->id());
self::assertArrayHasKey('#items', $build);
$expected_cache = [
'contexts' => [
'user',
'user.permissions',
],
'tags' => [
'tag_from_twig_tweak_test_node_access',
'node:1',
],
'max-age' => 50,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user without access check.
$build = $this->twigExtension->drupalField('title', 'node', $this->node->id(), 'default', NULL, FALSE);
self::assertArrayHasKey('#items', $build);
$expected_cache = [
'contexts' => [],
'tags' => ['node:1'],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
}
/**
* Test callback.
*/
public function testDrupalEntityEditForm() {
// -- Unprivileged user with access check.
$this->setUpCurrentUser(['name' => 'User 1']);
$build = $this->twigExtension->drupalEntityForm('node', $this->node->id());
self::assertNull($build);
// -- Unprivileged user without access check.
$build = $this->twigExtension->drupalEntityForm('node', $this->node->id(), 'default', [], FALSE);
self::assertArrayHasKey('form_id', $build);
$expected_cache = [
'contexts' => ['user.roles:authenticated'],
'tags' => [
'node:1',
'config:core.entity_form_display.node.article.default',
],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user with access check.
$this->setUpCurrentUser(['name' => 'User 2'], ['access content']);
$build = $this->twigExtension->drupalEntityForm('node', $this->node->id());
self::assertArrayHasKey('#form_id', $build);
$expected_cache = [
'contexts' => [
'user.roles:authenticated',
'user',
'user.permissions',
],
'tags' => [
'node:1',
'config:core.entity_form_display.node.article.default',
'tag_from_twig_tweak_test_node_access',
],
'max-age' => 50,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user without access check.
$build = $this->twigExtension->drupalEntityForm('node', $this->node->id(), 'default', [], FALSE);
self::assertArrayHasKey('#form_id', $build);
$expected_cache = [
'contexts' => ['user.roles:authenticated'],
'tags' => [
'node:1',
'config:core.entity_form_display.node.article.default',
],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
}
/**
* Test callback.
*/
public function testDrupalEntityAddForm() {
$node_values = ['type' => 'article'];
// -- Unprivileged user with access check.
$this->setUpCurrentUser(['name' => 'User 1']);
$build = $this->twigExtension->drupalEntityForm('node', NULL, 'default', $node_values);
self::assertNull($build);
// -- Unprivileged user without access check.
$build = $this->twigExtension->drupalEntityForm('node', NULL, 'default', $node_values, FALSE);
self::assertArrayHasKey('form_id', $build);
$expected_cache = [
'contexts' => ['user.roles:authenticated'],
'tags' => ['config:core.entity_form_display.node.article.default'],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user with access check.
$this->setUpCurrentUser(
['name' => 'User 2'],
['access content', 'create article content']
);
$build = $this->twigExtension->drupalEntityForm('node', NULL, 'default', $node_values);
self::assertArrayHasKey('form_id', $build);
$expected_cache = [
'contexts' => [
'user.roles:authenticated',
'user.permissions',
],
'tags' => ['config:core.entity_form_display.node.article.default'],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user without access check.
$build = $this->twigExtension->drupalEntityForm('node', NULL, 'default', $node_values);
self::assertArrayHasKey('form_id', $build);
$expected_cache = [
'contexts' => [
'user.roles:authenticated',
'user.permissions',
],
'tags' => ['config:core.entity_form_display.node.article.default'],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
}
/**
* Test callback.
*
* @see \Drupal\twig_tweak_test\Plugin\Block\FooBlock
*/
public function testDrupalBlock() {
// -- Privileged user.
$this->setUpCurrentUser(['name' => 'User 1']);
$build = $this->twigExtension->drupalBlock('twig_tweak_test_foo');
$expected_content = [
'#markup' => 'Foo',
'#cache' => [
'contexts' => ['url'],
'tags' => ['tag_from_build'],
],
];
self::assertSame($expected_content, $build['content']);
$expected_cache = [
'contexts' => ['user'],
'tags' => ['tag_from_blockAccess', 'tag_twig_tweak_test_foo_plugin'],
'max-age' => 35,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Unprivileged user.
$this->setUpCurrentUser(['name' => 'User 2']);
$build = $this->twigExtension->drupalBlock('twig_tweak_test_foo');
self::assertNull($build);
}
/**
* Test callback.
*/
public function testDrupalRegion() {
// @codingStandardsIgnoreStart
$create_block = function ($id) {
return new class(['id' => $id], 'block') extends Block {
public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = AccessResult::allowedIf($this->id == 'block_1');
$result->cachePerUser();
$result->addCacheTags(['tag_for_' . $this->id]);
$result->setCacheMaxAge(123);
return $return_as_object ? $result : $result->isAllowed();
}
public function getPlugin() {
return NULL;
}
};
};
// @codingStandardsIgnoreEnd
$storage = $this->createMock(EntityStorageInterface::class);
$blocks = [
'block_1' => $create_block('block_1'),
'block_2' => $create_block('block_2'),
];
$storage->expects($this->any())
->method('loadByProperties')
->willReturn($blocks);
$view_builder = $this->createMock(BlockViewBuilder::class);
$content = [
'#markup' => 'foo',
'#cache' => [
'tags' => ['tag_from_view'],
],
];
$view_builder->expects($this->any())
->method('view')
->willReturn($content);
$entity_type = $this->createMock(EntityTypeInterface::class);
$entity_type->expects($this->any())
->method('getListCacheTags')
->willReturn([]);
$entity_type->expects($this->any())
->method('getListCacheContexts')
->willReturn([]);
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$entity_type_manager->expects($this->any())
->method('getStorage')
->willReturn($storage);
$entity_type_manager->expects($this->any())
->method('getViewBuilder')
->willReturn($view_builder);
$entity_type_manager->expects($this->any())
->method('getDefinition')
->willReturn($entity_type);
$this->container->set('entity_type.manager', $entity_type_manager);
$build = $this->twigExtension->drupalRegion('bar');
$expected_build = [
'block_1' => [
'#markup' => 'foo',
'#cache' => [
'tags' => ['tag_from_view'],
],
],
'#region' => 'bar',
'#theme_wrappers' => ['region'],
'#cache' => [
'contexts' => ['user'],
'tags' => [
'tag_for_block_1',
'tag_for_block_2',
],
'max-age' => 123,
],
];
self::assertSame($expected_build, $build);
}
/**
* Test callback.
*/
public function testDrupalImage() {
// @codingStandardsIgnoreStart
$create_image = function ($uri) {
$file = new class(['uri' => $uri], 'file') extends File {
public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
$is_public = parse_url($this->getFileUri(), PHP_URL_SCHEME) == 'public';
$result = AccessResult::allowedIf($is_public);
$result->cachePerUser();
$result->addCacheTags(['tag_for_' . $this->getFileUri()]);
$result->setCacheMaxAge(123);
return $return_as_object ? $result : $result->isAllowed();
}
public function getPlugin() {
return NULL;
}
};
$file->setFileUri($uri);
return $file;
};
// @codingStandardsIgnoreEnd
$storage = $this->createMock(EntityStorageInterface::class);
$map = [
[
['uri' => 'public://ocean.jpg'],
[$create_image('public://ocean.jpg')],
],
[
['uri' => 'private://sea.jpg'],
[$create_image('private://sea.jpg')],
],
];
$storage->method('loadByProperties')
->will($this->returnValueMap($map));
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$entity_type_manager->method('getStorage')->willReturn($storage);
$this->container->set('entity_type.manager', $entity_type_manager);
// -- Public image with access check.
$build = $this->twigExtension->drupalImage('public://ocean.jpg');
$expected_build = [
'#uri' => 'public://ocean.jpg',
'#attributes' => [],
'#theme' => 'image',
'#cache' => [
'contexts' => ['user'],
'tags' => ['tag_for_public://ocean.jpg'],
'max-age' => 123,
],
];
self::assertSame($expected_build, $build);
// -- Public image without access check.
$build = $this->twigExtension->drupalImage('public://ocean.jpg', NULL, [], NULL, FALSE);
$expected_build = [
'#uri' => 'public://ocean.jpg',
'#attributes' => [],
'#theme' => 'image',
'#cache' => [
'contexts' => [],
'tags' => [],
'max-age' => Cache::PERMANENT,
],
];
self::assertSame($expected_build, $build);
// -- Private image with access check.
$build = $this->twigExtension->drupalImage('private://sea.jpg');
self::assertNull($build);
// -- Private image without access check.
$build = $this->twigExtension->drupalImage('private://sea.jpg', NULL, [], NULL, FALSE);
$expected_build = [
'#uri' => 'private://sea.jpg',
'#attributes' => [],
'#theme' => 'image',
'#cache' => [
'contexts' => [],
'tags' => [],
'max-age' => Cache::PERMANENT,
],
];
self::assertSame($expected_build, $build);
}
/**
* Test callback.
*/
public function testView() {
// -- Unprivileged user with access check.
$this->setUpCurrentUser(['name' => 'User 1']);
$build = $this->twigExtension->view($this->node);
self::assertNull($build);
// -- Unprivileged user without access check.
$build = $this->twigExtension->view($this->node, NULL, NULL, FALSE);
self::assertArrayHasKey('#node', $build);
$expected_cache = [
'tags' => [
'node_view',
'node:1',
],
'contexts' => [],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user with access check.
$this->setUpCurrentUser(['name' => 'User 2'], ['access content']);
$build = $this->twigExtension->view($this->node, NULL);
self::assertArrayHasKey('#node', $build);
$expected_cache = [
'tags' => [
'node_view',
'node:1',
'tag_from_twig_tweak_test_node_access',
],
'contexts' => [
'user',
'user.permissions',
],
'max-age' => 50,
];
self::assertSame($expected_cache, $build['#cache']);
// -- Privileged user without access check.
$build = $this->twigExtension->view($this->node, NULL, NULL, FALSE);
self::assertArrayHasKey('#node', $build);
$expected_cache = [
'tags' => [
'node_view',
'node:1',
],
'contexts' => [],
'max-age' => Cache::PERMANENT,
];
self::assertSame($expected_cache, $build['#cache']);
}
}

8
tests/twig_tweak_test/config/install/block.block.classy_powered_by_drupal.yml → tests/twig_tweak_test/config/install/block.block.claro_powered_by_drupal.yml

@ -4,10 +4,10 @@ dependencies:
module:
- system
theme:
- classy
id: classy_powered_by_drupal
theme: classy
region: sidebar_first
- claro
id: claro_powered_by_drupal
theme: claro
region: highlighted
weight: 20
provider: null
plugin: system_powered_by_block

17
tests/twig_tweak_test/config/install/block.block.classy_page_title.yml

@ -1,17 +0,0 @@
langcode: en
status: true
dependencies:
theme:
- classy
id: classy_page_title
theme: classy
region: sidebar_first
weight: 0
provider: null
plugin: page_title_block
settings:
id: page_title_block
label: 'Page title'
provider: core
label_display: '0'
visibility: { }

19
tests/twig_tweak_test/config/install/block.block.classy_status_messages.yml

@ -1,19 +0,0 @@
langcode: en
status: true
dependencies:
module:
- system
theme:
- classy
id: classy_status_messages
theme: classy
region: sidebar_first
weight: 10
provider: null
plugin: system_messages_block
settings:
id: system_messages_block
label: 'Status messages'
provider: system
label_display: '0'
visibility: { }

51
tests/twig_tweak_test/src/Plugin/Block/FooBlock.php

@ -0,0 +1,51 @@
<?php
namespace Drupal\twig_tweak_test\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Session\AccountInterface;
/**
* Provides a foo block.
*
* @Block(
* id = "twig_tweak_test_foo",
* admin_label = @Translation("Foo"),
* category = @Translation("Twig Tweak")
* )
*/
class FooBlock extends BlockBase {
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
$result = AccessResult::allowedIf($account->getAccountName() == 'User 1');
$result->addCacheTags(['tag_from_' . __FUNCTION__]);
$result->setCacheMaxAge(35);
$result->cachePerUser();
return $result;
}
/**
* {@inheritdoc}
*/
public function build() {
return [
'#markup' => 'Foo',
'#cache' => [
'contexts' => ['url'],
'tags' => ['tag_from_' . __FUNCTION__],
],
];
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
return ['tag_twig_tweak_test_foo_plugin'];
}
}

12
tests/twig_tweak_test/templates/twig-tweak-test.html.twig

@ -23,10 +23,10 @@
<div class="tt-view-result">{{ drupal_view_result('twig_tweak_test', 'page_1')|length }}</div>
<div class="tt-block">{{ drupal_block('system_branding_block', {use_site_name: false}, false) }}</div>
<div class="tt-block-with-wrapper">{{ drupal_block('system_branding_block', {label: 'Branding'}) }}</div>
<div class="tt-region">{{ drupal_region('sidebar_first') }}</div>
<div class="tt-region">{{ drupal_region('highlighted') }}</div>
<div class="tt-entity-default">{{ drupal_entity('node', 1) }}</div>
<div class="tt-entity-teaser">{{ drupal_entity('node', 1, 'teaser') }}</div>
<div class="tt-entity-from-url">{{ drupal_entity('node') }}</div>
<div class="tt-entity-from-url">{{ drupal_entity('node', view_mode='teaser') }}</div>
<div class="tt-entity-add-form">{{ drupal_entity_form('node', values={type: 'page'}) }}</div>
<div class="tt-entity-edit-form">{{ drupal_entity_form('node', 1) }}</div>
<div class="tt-field">{{ drupal_field('body', 'node', 1) }}</div>
@ -58,6 +58,7 @@
<div class="tt-image-style">{{ 'public://images/ocean.jpg' | image_style('thumbnail') }}</div>
<div class="tt-transliterate">{{ 'Привет!' | transliterate('ru') }}</div>
<div class="tt-check-markup">{{ '<b>bold</b> <strong>strong</strong>' | check_markup('twig_tweak_test') }}</div>
<div class="tt-format-size">{{ 12345|format_size() }}</div>
<div class="tt-truncate">{{ 'Hello world!'|truncate(10, true, true) }}</div>
<div class="tt-with">{{ {'#markup':'Example'}|with('#prefix', '<b>')|with('#suffix', '</b>') }}</div>
<div class="tt-with-nested">{{ {alpha: {beta: {gamma: 123}}}|with(['alpha', 'beta', 'gamma'], 456)|json_encode|replace({'"':''}) }}</div>
@ -78,6 +79,13 @@
<div class="tt-node-view">{{ node|view }}</div>
<div class="tt-field-list-view">{{ node.title|view }}</div>
<div class="tt-field-item-view">{{ node.title[0]|view }}</div>
<div class="tt-file-uri-from-image-field">{{ node.field_image|file_uri }}</div>
<div class="tt-file-uri-from-image-field-delta">{{ node.field_image[0]|file_uri }}</div>
{% set media_uri = node.field_media|file_uri %}
<div class="tt-file-uri-from-media-field">{{ media_uri }}</div>
{% if media_uri is not null %}
<div class="tt-image-style-from-file-uri-from-media-field">{{ media_uri|image_style('thumbnail') }}</div>
{% endif %}
<div class="tt-file-url-from-uri">{{ 'public://image-test.png'|file_url }}</div>
<div class="tt-file-url-from-image-field">{{ node.field_image|file_url }}</div>
<div class="tt-file-url-from-image-field-delta">{{ node.field_image[0]|file_url }}</div>

18
tests/twig_tweak_test/twig_tweak_test.module

@ -5,6 +5,9 @@
* Primary module hooks for Twig Tweak test module.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\node\NodeInterface;
/**
* Implements hook_page_bottom().
*/
@ -26,3 +29,18 @@ function twig_tweak_test_theme() {
function template_preprocess_twig_tweak_test(&$vars) {
$vars['node'] = Drupal::routeMatch()->getParameter('node');
}
/**
* Implements hook_node_access().
*
* @see \Drupal\Tests\twig_tweak\Kernel\AccessTest
*/
function twig_tweak_test_node_access(NodeInterface $node) {
if ($node->getTitle() == 'Entity access test') {
$result = AccessResult::allowed();
$result->addCacheTags(['tag_from_' . __FUNCTION__]);
$result->cachePerUser();
$result->setCacheMaxAge(50);
return $result;
}
}

Loading…
Cancel
Save