g2-8.x-1.x-dev/tests/src/Kernel/WOTDTest.php
tests/src/Kernel/WOTDTest.php
<?php
declare(strict_types=1);
namespace Drupal\Tests\g2\Kernel;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Url;
use Drupal\g2\G2;
use Drupal\g2\Plugin\Block\WotdBlock;
use Drupal\g2\TestLogger;
use Drupal\g2\WOTD;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\NodeInterface;
use Drupal\Tests\node\Traits\NodeCreationTrait;
/**
* Tests for the WOTD service.
*
* @group G2
*/
class WOTDTest extends KernelTestBase {
use NodeCreationTrait {
createNode as drupalCreateNode;
}
/**
* The title of the WOTD node.
*/
const TITLE_WOTD = 'WOTD';
const MODULES = [
// Needed for routing.
'system',
// Service node_preview (proxied) needs user.private_tempstore.
'user',
// Needed by text.module.
'field',
'filter',
// Needed by node module.
'text',
// Needed by g2.module.
'node',
'path_alias',
'taxonomy',
'views',
'g2',
];
/**
* The modules to enable for the test.
*
* @var string[]
*/
protected static $modules = self::MODULES;
/**
* The config.factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected ConfigFactoryInterface $config;
/**
* The entity_type.manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $etm;
/**
* The core plugin.manager.block service.
*
* @var \Drupal\Core\Block\BlockManagerInterface
*/
protected BlockManagerInterface $pmBlock;
/**
* The core state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected StateInterface $state;
/**
* The test logger, not the logger.channel.g2 service.
*
* @var \Drupal\g2\TestLogger
*/
protected TestLogger $testLogger;
/**
* The g2.wotd service.
*
* @var \Drupal\g2\WOTD
*/
protected WOTD $wotd;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('path_alias');
$this->installEntitySchema('user');
$this->installSchema('node', []);
$this->installConfig(static::MODULES);
// @see https://www.drupal.org/node/2605684
$this->container->get('router.builder')->rebuild();
$this->config = $this->container->get(G2::SVC_CONF);
$this->etm = $this->container->get(G2::SVC_ETM);
$this->pmBlock = $this->container->get(G2::SVC_PM_BLOCK);
$this->state = $this->container->get(G2::SVC_STATE);
$this->wotd = $this->container->get(G2::SVC_WOTD);
$this->testLogger = $this->container->get(G2::SVC_TEST_LOGGER);
/** @var \Drupal\Core\Logger\LoggerChannelInterface $logger */
$logger = $this->container->get(G2::SVC_LOGGER);
$logger->addLogger($this->testLogger);
}
/**
* Data provider for testGet().
*
* @return array<string,array{boolean,boolean,string}>
* Cf. test.
*/
public static function providerGet(): array {
return [
'not stored' => [FALSE, TRUE, ''],
'stored' => [TRUE, FALSE, static::TITLE_WOTD],
];
}
/**
* Test WOTD::get.
*
* @dataProvider providerGet
*/
public function testGet(
bool $isWOTDStored,
bool $expectNull,
string $expectedTitle,
): void {
// Setup: content.
$wotd = $this->drupalCreateNode([
'title' => static::TITLE_WOTD,
'type' => G2::BUNDLE,
]);
// Setup: config.
$conf = $this->config->getEditable(G2::CONFIG_NAME);
if ($isWOTDStored) {
$conf->set(G2::VARWOTDENTRY, $wotd->id());
}
else {
$conf->clear(G2::VARWOTDENTRY);
}
$conf->save();
// Perform test.
try {
$actual = $this->wotd->get();
}
catch (\Exception $e) {
$this->fail(sprintf("Unexpected exception: %s", $e));
}
if ($expectNull) {
if (!is_null($actual)) {
$this->fail(sprintf('Expected no WOTD but got "%s"', $actual->label()));
}
return;
}
$actualTitle = ($actual instanceof NodeInterface)
? $actual->label()
: '';
$this->assertEquals($expectedTitle, $actualTitle);
}
/**
* Data provider for testIsAutoChangeEnabled.
*
* @return array<string,array{?boolean,boolean}>
* Cf. test.
*/
public static function providerIsAutoChangeEnabled(): array {
return [
"disabled" => [TRUE, TRUE],
"enabled" => [FALSE, FALSE],
"not set" => [NULL, FALSE],
];
}
/**
* Test WOTD::isAutoChangeEnabled.
*
* @param bool|null $input
* Input if any.
* @param bool $expected
* Expected auto-change to be enabled.
*
* @throws \Exception
*
* @dataProvider providerIsAutoChangeEnabled
*/
public function testIsAutoChangeEnabled(?bool $input, bool $expected): void {
$config = $this->config->getEditable(G2::CONFIG_NAME);
if (!isset($input)) {
$config->clear(G2::VARWOTDAUTOCHANGE);
}
else {
$config->set(G2::VARWOTDAUTOCHANGE, $input);
}
$config->save();
$actual = $this->wotd->isAutoChangeEnabled();
$this->assertEquals($expected, $actual);
}
/**
* Data provider for testAutochange.
*
* @return array<string,array{string[],boolean,\DateTimeImmutable,boolean,string}>
* Cf. test.
*/
public static function providerAutoChange(): array {
$now = \DateTimeImmutable::createFromFormat('U', (string) (time() - 86400));
self::assertInstanceOf(\DateTimeImmutable::class, $now);
$yesterday = $now->sub(new \DateInterval("P1D"));
assert($yesterday instanceof \DateTimeImmutable);
return [
'happy' => [
[
RandomTest::TITLE_FOO,
RandomTest::TITLE_WOTD,
],
TRUE,
$yesterday,
FALSE,
RandomTest::TITLE_FOO,
],
'sad no node available' => [
[RandomTest::TITLE_WOTD],
TRUE,
$yesterday,
TRUE,
'',
],
];
}
/**
* Test WOTD::autoChange.
*
* @param string[] $nodesToCreate
* The titles for the nodes to create.
* @param bool $isEntrySet
* Set the WOTD entry or not.
* @param \DateTimeImmutable $entryDate
* Timestamp for the entry if set.
* @param bool $expectError
* Expect an error.
* @param string $expectedTitle
* Expected title.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Exception
*
* @dataProvider providerAutoChange
*/
public function testAutochange(
array $nodesToCreate,
bool $isEntrySet,
\DateTimeImmutable $entryDate,
bool $expectError,
string $expectedTitle,
): void {
$nodesByTitle = [];
foreach ($nodesToCreate as $title) {
$nodesByTitle[$title] = $this->drupalCreateNode([
'title' => $title,
'type' => G2::BUNDLE,
]);
}
$this->assertNotEmpty($nodesByTitle[RandomTest::TITLE_WOTD]);
if ($isEntrySet) {
$this->assertNotEmpty($entryDate);
$this->config
->getEditable(G2::CONFIG_NAME)
->set(G2::VARWOTDENTRY, $nodesByTitle[RandomTest::TITLE_WOTD]->id())
->save();
$this->state->set(G2::VARWOTDDATE, $entryDate->format(WOTD::DATE_STORAGE_FORMAT));
}
$this->wotd->autoChange();
$errorCount = $this->testLogger->countByLevel(RfcLogLevel::ERROR);
if ($expectError) {
$this->assertEquals(1, $errorCount);
return;
}
$this->assertEquals(0, $errorCount);
if (!$expectedTitle) {
return;
}
$actualNid = $this->config
->get(G2::CONFIG_NAME)
->get(G2::VARWOTDENTRY);
$this->assertNotEmpty($actualNid);
$actualNode = $this->etm
->getStorage(G2::TYPE)
->load($actualNid);
$this->assertNotEmpty($actualNode);
$actualTitle = $actualNode->label();
$this->assertEquals($expectedTitle, $actualTitle);
}
/**
* Data provider for testPreprocessFeedIcon().
*
* @return array<string,mixed>
* Test cases.
*/
public static function providerPreprocessFeedIcon(): array {
global $base_url;
$tests = [
'all empty' => [[], FALSE, FALSE, FALSE],
'external string URL' => [
['url' => 'https://example.com'], FALSE, FALSE, FALSE,
],
'internal string URL, invalid' => [
['url' => "{$base_url}/invalid"], FALSE, FALSE, FALSE,
],
'internal string URL, unrouted' => [
['url' => "{$base_url}/robots.txt"], FALSE, FALSE, FALSE,
],
'internal string URL, other route' => [
['url' => "{$base_url}/node"], FALSE, FALSE, FALSE,
],
'internal string URL, WOTD feed' => [
['url' => '/g2/feed/wotd'], FALSE, TRUE, TRUE,
],
'URL object, unrouted' => [
['url' => 'base:robots.txt'], TRUE, FALSE, FALSE,
],
'URL object, WOTD feed' => [
['url' => G2::ROUTE_FEED_WOTD], TRUE, TRUE, TRUE,
],
];
return $tests;
}
/**
* Preprocesses the feed icon link based on the given input variables.
*
* @param array<string,mixed> $variables
* The relevant sub-array of $variables on input.
* @param bool $asURL
* Does test need to generate a URL from the URL string?
* @param bool $expectAttributes
* Must the test expect an "attributes" key in $variables?
* @param bool $expectedClass
* Must the test expect the WOTDBlock::ICON_CLASS in the class attributes?
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*
* @dataProvider providerPreprocessFeedIcon
*/
public function testPreprocessFeedIcon(array $variables, bool $asURL, bool $expectAttributes, bool $expectedClass): void {
/** @var \Drupal\g2\Plugin\Block\WotdBlock $block */
$block = $this->pmBlock->createInstance(G2::DELTA_WOTD);
if ($asURL) {
$uri = $variables['url'] ?? '';
$this->assertIsString($uri);
if (str_starts_with($uri, 'base:')) {
$url = Url::fromUri($uri);
}
else {
$url = Url::fromRoute($uri);
}
$variables['url'] = $url;
}
$block->preprocessFeedIcon($variables);
if (!$expectAttributes) {
$this->assertArrayNotHasKey('attributes', $variables);
return;
}
$this->assertArrayHasKey('attributes', $variables);
$attributes = $variables['attributes'];
$this->assertArrayHasKey('class', $attributes);
$classes = $attributes['class'];
$this->assertIsArray($classes);
$this->assertEquals($expectedClass,
in_array(WotdBlock::ICON_CLASS, $classes));
}
}
