first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

19786
core/modules/forum/tests/fixtures/drupal6.php vendored Executable file

File diff suppressed because it is too large Load Diff

30598
core/modules/forum/tests/fixtures/drupal7.php vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
name: 'Forum test views'
type: module
description: 'Provides default views for views forum tests.'
package: Testing
# version: VERSION
dependencies:
- drupal:forum
- drupal:views
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,148 @@
langcode: en
status: true
dependencies: { }
id: test_forum_index
label: test_forum_index
module: views
description: ''
tag: ''
base_table: forum_index
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: null
display_options:
access:
type: none
cache:
type: tag
query:
type: views_query
exposed_form:
type: basic
pager:
type: full
style:
type: default
row:
type: fields
fields:
nid:
table: forum_index
field: nid
id: nid
sticky:
id: sticky
table: forum_index
field: sticky
relationship: none
group_type: group
admin_label: ''
label: Sticky
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
type: yes-no
type_custom_true: ''
type_custom_false: ''
not: false
plugin_id: boolean
comment_count:
id: comment_count
table: forum_index
field: comment_count
relationship: none
group_type: group
admin_label: ''
label: 'Comment count'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
set_precision: false
precision: 0
decimal: .
separator: ','
format_plural: false
format_plural_string: "1\x03@count"
prefix: ''
suffix: ''
plugin_id: numeric
filters: { }
sorts: { }

View File

@@ -0,0 +1,10 @@
name: 'forum_Url_alter tests'
type: module
description: 'A support module to test altering the inbound and outbound path.'
package: Testing
# version: VERSION
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,6 @@
services:
forum_url_alter_test.path_processor:
class: Drupal\forum_url_alter_test\PathProcessorTest
tags:
- { name: path_processor_inbound, priority: 800 }
- { name: path_processor_outbound, priority: 100 }

View File

@@ -0,0 +1,31 @@
<?php
namespace Drupal\forum_url_alter_test;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Symfony\Component\HttpFoundation\Request;
/**
* Path processor for url_alter_test.
*/
class PathProcessorTest implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
/**
* {@inheritdoc}
*/
public function processInbound($path, Request $request) {
// Rewrite community/ to forum/.
return preg_replace('@^/community(.*)@', '/forum$1', $path);
}
/**
* {@inheritdoc}
*/
public function processOutbound($path, &$options = [], ?Request $request = NULL, ?BubbleableMetadata $bubbleable_metadata = NULL) {
// Rewrite forum/ to community/.
return preg_replace('@^/forum(.*)@', '/community$1', $path);
}
}

View File

@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\comment\Entity\Comment;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the forum blocks.
*
* @group forum
* @group legacy
*/
class ForumBlockTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['forum', 'block'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* A user with various administrative privileges.
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create users.
$this->adminUser = $this->drupalCreateUser([
'access administration pages',
'administer blocks',
'administer nodes',
'create forum content',
'post comments',
'skip comment approval',
]);
}
/**
* Tests the "New forum topics" block.
*/
public function testNewForumTopicsBlock(): void {
$this->drupalLogin($this->adminUser);
// Enable the new forum topics block.
$block = $this->drupalPlaceBlock('forum_new_block');
$this->drupalGet('');
// Create 5 forum topics.
$topics = $this->createForumTopics();
$this->assertSession()->linkExists('More', 0, 'New forum topics block has a "more"-link.');
$this->assertSession()->linkByHrefExists('forum', 0, 'New forum topics block has a "more"-link.');
// We expect all 5 forum topics to appear in the "New forum topics" block.
foreach ($topics as $topic) {
$this->assertSession()->linkExists($topic, 0, new FormattableMarkup('Forum topic @topic found in the "New forum topics" block.', ['@topic' => $topic]));
}
// Configure the new forum topics block to only show 2 topics.
$block->getPlugin()->setConfigurationValue('block_count', 2);
$block->save();
$this->drupalGet('');
// We expect only the 2 most recent forum topics to appear in the "New forum
// topics" block.
for ($index = 0; $index < 5; $index++) {
if (in_array($index, [3, 4])) {
$this->assertSession()->linkExists($topics[$index], 0, new FormattableMarkup('Forum topic @topic found in the "New forum topics" block.', ['@topic' => $topics[$index]]));
}
else {
$this->assertSession()->pageTextNotContains($topics[$index]);
}
}
}
/**
* Tests the "Active forum topics" block.
*/
public function testActiveForumTopicsBlock(): void {
$this->drupalLogin($this->adminUser);
// Create 10 forum topics.
$topics = $this->createForumTopics(10);
// Comment on the first 5 topics.
$date = new DrupalDateTime();
for ($index = 0; $index < 5; $index++) {
// Get the node from the topic title.
$node = $this->drupalGetNodeByTitle($topics[$index]);
$date->modify('+1 minute');
$comment = Comment::create([
'entity_id' => $node->id(),
'field_name' => 'comment_forum',
'entity_type' => 'node',
'node_type' => 'node_type_' . $node->bundle(),
'subject' => $this->randomString(20),
'comment_body' => $this->randomString(256),
'created' => $date->getTimestamp(),
]);
$comment->save();
}
// Enable the block.
$block = $this->drupalPlaceBlock('forum_active_block');
$this->drupalGet('');
$this->assertSession()->linkExists('More', 0, 'Active forum topics block has a "more"-link.');
$this->assertSession()->linkByHrefExists('forum', 0, 'Active forum topics block has a "more"-link.');
// We expect the first 5 forum topics to appear in the "Active forum topics"
// block.
$this->drupalGet('<front>');
for ($index = 0; $index < 10; $index++) {
if ($index < 5) {
$this->assertSession()->linkExists($topics[$index], 0, new FormattableMarkup('Forum topic @topic found in the "Active forum topics" block.', ['@topic' => $topics[$index]]));
}
else {
$this->assertSession()->pageTextNotContains($topics[$index]);
}
}
// Configure the active forum block to only show 2 topics.
$block->getPlugin()->setConfigurationValue('block_count', 2);
$block->save();
$this->drupalGet('');
// We expect only the 2 forum topics with most recent comments to appear in
// the "Active forum topics" block.
for ($index = 0; $index < 10; $index++) {
if (in_array($index, [3, 4])) {
$this->assertSession()->linkExists($topics[$index], 0, 'Forum topic found in the "Active forum topics" block.');
}
else {
$this->assertSession()->pageTextNotContains($topics[$index]);
}
}
}
/**
* Creates a forum topic.
*
* @return string
* The title of the newly generated topic.
*/
protected function createForumTopics($count = 5) {
$topics = [];
$date = new DrupalDateTime();
$date->modify('-24 hours');
for ($index = 0; $index < $count; $index++) {
// Generate a random subject/body.
$title = $this->randomMachineName(20);
$body = $this->randomMachineName(200);
// Forum posts are ordered by timestamp, so force a unique timestamp by
// changing the date.
$date->modify('+1 minute');
$edit = [
'title[0][value]' => $title,
'body[0][value]' => $body,
// Forum posts are ordered by timestamp, so force a unique timestamp by
// adding the index.
'created[0][value][date]' => $date->format('Y-m-d'),
'created[0][value][time]' => $date->format('H:i:s'),
];
// Create the forum topic, preselecting the forum ID via a URL parameter.
$this->drupalGet('node/add/forum', ['query' => ['forum_id' => 1]]);
$this->submitForm($edit, 'Save');
$topics[] = $title;
}
return $topics;
}
}

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the forum index listing.
*
* @group forum
* @group legacy
*/
class ForumIndexTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy', 'comment', 'forum'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create a test user.
$web_user = $this->drupalCreateUser([
'create forum content',
'edit own forum content',
'edit any forum content',
'administer nodes',
'administer forums',
]);
$this->drupalLogin($web_user);
}
/**
* Tests the forum index for published and unpublished nodes.
*/
public function testForumIndexStatus(): void {
// The forum ID to use.
$tid = 1;
// Create a test node.
$title = $this->randomMachineName(20);
$edit = [
'title[0][value]' => $title,
'body[0][value]' => $this->randomMachineName(200),
];
// Create the forum topic, preselecting the forum ID via a URL parameter.
$this->drupalGet("forum/$tid");
$this->clickLink('Add new Forum topic');
$this->assertSession()->addressEquals("node/add/forum?forum_id=$tid");
$this->submitForm($edit, 'Save');
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($title);
$this->assertNotEmpty($node, 'New forum node found in database.');
// Create a child forum.
$edit = [
'name[0][value]' => $this->randomMachineName(20),
'description[0][value]' => $this->randomMachineName(200),
'parent[0]' => $tid,
];
$this->drupalGet('admin/structure/forum/add/forum');
$this->submitForm($edit, 'Save');
$this->assertSession()->linkExists('edit forum');
$tid_child = $tid + 1;
// Verify that the node appears on the index.
$this->drupalGet('forum/' . $tid);
$this->assertSession()->pageTextContains($title);
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'node_list');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:node.type.forum');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'comment_list');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'node:' . $node->id());
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'taxonomy_term:' . $tid);
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'taxonomy_term:' . $tid_child);
// Unpublish the node.
$edit = ['status[value]' => FALSE];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->drupalGet('node/' . $node->id());
$this->assertSession()->pageTextContains('Access denied');
// Verify that the node no longer appears on the index.
$this->drupalGet('forum/' . $tid);
$this->assertSession()->pageTextNotContains($title);
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Core\Site\Settings;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests addition of the forum_index primary key.
*
* @group forum
*/
final class ForumIndexUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
dirname(__DIR__, 2) . '/fixtures/update/drupal-10.1.0.empty.testing.forum.gz',
];
}
/**
* Tests the update path to add the new primary key.
*/
public function testUpdatePath(): void {
// Set the batch size to 1 to validate the sandbox logic in the update hook.
$settings = Settings::getInstance() ? Settings::getAll() : [];
$settings['entity_update_batch_size'] = 1;
new Settings($settings);
$schema = \Drupal::database()->schema();
// We can't reliably call ::indexExists for each database driver as sqlite
// doesn't have named indexes for primary keys like mysql (PRIMARY) and
// pgsql (pkey).
$find_primary_key_columns = new \ReflectionMethod(get_class($schema), 'findPrimaryKeyColumns');
$columns = $find_primary_key_columns->invoke($schema, 'forum_index');
$this->assertEmpty($columns);
$count = \Drupal::database()->select('forum_index')->countQuery()->execute()->fetchField();
$this->assertEquals(9, $count);
$duplicates = \Drupal::database()->select('forum_index')->condition('nid', 1)->countQuery()->execute()->fetchField();
$this->assertEquals(2, $duplicates);
$duplicates = \Drupal::database()->select('forum_index')->condition('nid', 2)->countQuery()->execute()->fetchField();
$this->assertEquals(3, $duplicates);
$this->runUpdates();
$this->assertEquals(['nid', 'tid'], $find_primary_key_columns->invoke($schema, 'forum_index'));
$count = \Drupal::database()->select('forum_index')->countQuery()->execute()->fetchField();
$this->assertEquals(6, $count);
$duplicates = \Drupal::database()->select('forum_index')->condition('nid', 1)->countQuery()->execute()->fetchField();
$this->assertEquals(1, $duplicates);
$duplicates = \Drupal::database()->select('forum_index')->condition('nid', 2)->countQuery()->execute()->fetchField();
$this->assertEquals(1, $duplicates);
// This entry is associated with two terms so two records should remain.
$duplicates = \Drupal::database()->select('forum_index')->condition('nid', 4)->countQuery()->execute()->fetchField();
$this->assertEquals(2, $duplicates);
$entry = \Drupal::database()->select('forum_index', 'f')->fields('f')->condition('nid', 5)->execute()->fetchAssoc();
$this->assertEquals([
'nid' => 5,
'title' => 'AFL',
'tid' => 5,
'sticky' => 0,
'created' => 1695264369,
'last_comment_timestamp' => 1695264403,
'comment_count' => 1,
], $entry);
}
}

View File

@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\node\Entity\NodeType;
/**
* Tests forum block view for private node access.
*
* @group forum
* @group legacy
*/
class ForumNodeAccessTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'node',
'comment',
'forum',
'taxonomy',
'node_access_test',
'block',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
node_access_rebuild();
node_access_test_add_field(NodeType::load('forum'));
\Drupal::state()->set('node_access_test.private', TRUE);
}
/**
* Creates some users and creates a public node and a private node.
*
* Adds both active forum topics and new forum topics blocks to the sidebar.
* Tests to ensure private node/public node access is respected on blocks.
*/
public function testForumNodeAccess(): void {
// Create some users.
$access_user = $this->drupalCreateUser(['node test view']);
$no_access_user = $this->drupalCreateUser();
$admin_user = $this->drupalCreateUser([
'access administration pages',
'administer modules',
'administer blocks',
'create forum content',
]);
$this->drupalLogin($admin_user);
// Create a private node.
$private_node_title = $this->randomMachineName(20);
$edit = [
'title[0][value]' => $private_node_title,
'body[0][value]' => $this->randomMachineName(200),
'private[0][value]' => TRUE,
];
$this->drupalGet('node/add/forum', ['query' => ['forum_id' => 1]]);
$this->submitForm($edit, 'Save');
$private_node = $this->drupalGetNodeByTitle($private_node_title);
$this->assertNotEmpty($private_node, 'New private forum node found in database.');
// Create a public node.
$public_node_title = $this->randomMachineName(20);
$edit = [
'title[0][value]' => $public_node_title,
'body[0][value]' => $this->randomMachineName(200),
];
$this->drupalGet('node/add/forum', ['query' => ['forum_id' => 1]]);
$this->submitForm($edit, 'Save');
$public_node = $this->drupalGetNodeByTitle($public_node_title);
$this->assertNotEmpty($public_node, 'New public forum node found in database.');
// Enable the new and active forum blocks.
$this->drupalPlaceBlock('forum_active_block');
$this->drupalPlaceBlock('forum_new_block');
// Test for $access_user.
$this->drupalLogin($access_user);
$this->drupalGet('');
// Ensure private node and public node are found.
$this->assertSession()->pageTextContains($private_node->getTitle());
$this->assertSession()->pageTextContains($public_node->getTitle());
// Test for $no_access_user.
$this->drupalLogin($no_access_user);
$this->drupalGet('');
// Ensure private node is not found but public is found.
$this->assertSession()->pageTextNotContains($private_node->getTitle());
$this->assertSession()->pageTextContains($public_node->getTitle());
}
}

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Term;
/**
* Tests forum taxonomy terms for access.
*
* @group forum
* @group legacy
*/
class ForumTermAccessTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'forum',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Creates some users and creates a public forum and an unpublished forum.
*
* Adds both published and unpublished forums.
* Tests to ensure publish/unpublished forums access is respected.
*/
public function testForumTermAccess(): void {
$assert_session = $this->assertSession();
// Create some users.
$public_user = $this->drupalCreateUser(['access content']);
$admin_user = $this->drupalCreateUser([
'access administration pages',
'administer forums',
'administer taxonomy',
'access taxonomy overview',
]);
$this->drupalLogin($admin_user);
// The vocabulary for forums.
$vid = $this->config('forum.settings')->get('vocabulary');
// Create an unpublished forum.
$unpublished_forum_name = $this->randomMachineName(8);
$unpublished_forum = Term::create([
'vid' => $vid,
'name' => $unpublished_forum_name,
'status' => 0,
]);
$unpublished_forum->save();
// Create a new published forum.
$published_forum_name = $this->randomMachineName(8);
$published_forum = Term::create([
'vid' => $vid,
'name' => $published_forum_name,
'status' => 1,
]);
$published_forum->save();
// Test for admin user.
// Go to the Forum index page.
$this->drupalGet('forum');
// The unpublished forum should be in this page for an admin user.
$assert_session->pageTextContains($unpublished_forum_name);
// Go to the unpublished forum page.
$this->drupalGet('forum/' . $unpublished_forum->id());
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains($unpublished_forum_name);
// Test for public user.
$this->drupalLogin($public_user);
// Go to the Forum index page.
$this->drupalGet('forum');
// The published forum should be in this page.
$assert_session->pageTextContains($published_forum_name);
// The unpublished forum should not be in this page.
$assert_session->pageTextNotContains($unpublished_forum_name);
// Go to the unpublished forum page.
$this->drupalGet('forum/' . $unpublished_forum->id());
// Public should not be able to access the unpublished forum.
$assert_session->statusCodeEquals(403);
$assert_session->pageTextNotContains($unpublished_forum_name);
}
}

View File

@@ -0,0 +1,753 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\BrowserTestBase;
/**
* Tests for forum.module.
*
* Create, view, edit, delete, and change forum entries and verify its
* consistency in the database.
*
* @group forum
* @group legacy
* @group #slow
*/
class ForumTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'taxonomy',
'comment',
'forum',
'node',
'block',
'menu_ui',
'help',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'starterkit_theme';
/**
* A user with various administrative privileges.
*/
protected $adminUser;
/**
* A user that can create forum topics and edit its own topics.
*/
protected $editOwnTopicsUser;
/**
* A user that can create, edit, and delete forum topics.
*/
protected $editAnyTopicsUser;
/**
* A user with no special privileges.
*/
protected $webUser;
/**
* An administrative user who can bypass comment approval.
*/
protected $postCommentUser;
/**
* An array representing a forum container.
*/
protected $forumContainer;
/**
* An array representing a forum.
*/
protected $forum;
/**
* An array representing a root forum.
*/
protected $rootForum;
/**
* An array of forum topic node IDs.
*/
protected $nids;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
$this->drupalPlaceBlock('page_title_block');
// Create users.
$this->adminUser = $this->drupalCreateUser([
'access administration pages',
'access help pages',
'administer modules',
'administer blocks',
'administer forums',
'administer menu',
'administer taxonomy',
'create forum content',
'access comments',
]);
$this->editAnyTopicsUser = $this->drupalCreateUser([
'access administration pages',
'access help pages',
'create forum content',
'edit any forum content',
'delete any forum content',
]);
$this->editOwnTopicsUser = $this->drupalCreateUser([
'create forum content',
'edit own forum content',
'delete own forum content',
]);
$this->webUser = $this->drupalCreateUser();
$this->postCommentUser = $this->drupalCreateUser([
'administer content types',
'create forum content',
'post comments',
'skip comment approval',
'access comments',
]);
$this->drupalPlaceBlock('help_block', ['region' => 'help']);
$this->drupalPlaceBlock('local_actions_block');
}
/**
* Tests forum functionality through the admin and user interfaces.
*/
public function testForum(): void {
// Check that the basic forum install creates a default forum topic
$this->drupalGet('/forum');
// Look for the "General discussion" default forum
$this->assertSession()->linkExists('General discussion');
$this->assertSession()->linkByHrefExists('/forum/1');
// Check the presence of expected cache tags.
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:forum.settings');
$this->drupalGet(Url::fromRoute('forum.page', ['taxonomy_term' => 1]));
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:forum.settings');
// Do the admin tests.
$this->doAdminTests($this->adminUser);
// Check display order.
$display = EntityViewDisplay::load('node.forum.default');
$body = $display->getComponent('body');
$comment = $display->getComponent('comment_forum');
$taxonomy = $display->getComponent('taxonomy_forums');
// Assert field order is body » taxonomy » comments.
$this->assertLessThan($body['weight'], $taxonomy['weight']);
$this->assertLessThan($comment['weight'], $body['weight']);
// Check form order.
$display = EntityFormDisplay::load('node.forum.default');
$body = $display->getComponent('body');
$comment = $display->getComponent('comment_forum');
$taxonomy = $display->getComponent('taxonomy_forums');
// Assert category comes before body in order.
$this->assertLessThan($body['weight'], $taxonomy['weight']);
$this->generateForumTopics();
// Log in an unprivileged user to view the forum topics and generate an
// active forum topics list.
$this->drupalLogin($this->webUser);
// Verify that this user is shown a message that they may not post content.
$this->drupalGet('forum/' . $this->forum['tid']);
$this->assertSession()->pageTextContains('You are not allowed to post new content in the forum');
// Log in, and do basic tests for a user with permission to edit any forum
// content.
$this->doBasicTests($this->editAnyTopicsUser, TRUE);
// Create a forum node authored by this user.
$any_topics_user_node = $this->createForumTopic($this->forum, FALSE);
// Log in, and do basic tests for a user with permission to edit only its
// own forum content.
$this->doBasicTests($this->editOwnTopicsUser, FALSE);
// Create a forum node authored by this user.
$own_topics_user_node = $this->createForumTopic($this->forum, FALSE);
// Verify that this user cannot edit forum content authored by another user.
$this->verifyForums($any_topics_user_node, FALSE, 403);
// Verify that this user is shown a local task to add new forum content.
$this->drupalGet('forum');
$this->assertSession()->linkExists('Add new Forum topic');
$this->drupalGet('forum/' . $this->forum['tid']);
$this->assertSession()->linkExists('Add new Forum topic');
// Log in a user with permission to edit any forum content.
$this->drupalLogin($this->editAnyTopicsUser);
// Verify that this user can edit forum content authored by another user.
$this->verifyForums($own_topics_user_node, TRUE);
// Verify the topic and post counts on the forum page.
$this->drupalGet('forum');
// Find the table row for the forum that has new posts. This cannot be
// reliably identified by any CSS selector or its position in the table,
// so look for an element with the "New posts" title and traverse up the
// document tree until we get to the table row that contains it.
$row = $this->assertSession()->elementExists('css', '[title="New posts"]');
while ($row && $row->getTagName() !== 'tr') {
$row = $row->getParent();
}
$this->assertNotEmpty($row);
$cells = $row->findAll('css', 'td');
$this->assertCount(4, $cells);
// Topics cell contains number of topics (6), number of unread topics (also
// 6), and the forum name.
$this->assertEquals('6 6 new posts in forum ' . $this->forum['name'], $cells[1]->getText(), 'Number of topics found.');
// Verify total number of posts in forum.
$this->assertEquals('6', $cells[2]->getText(), 'Number of posts found.');
// Test loading multiple forum nodes on the front page.
$this->drupalLogin($this->drupalCreateUser([
'administer content types',
'create forum content',
'post comments',
]));
$this->drupalGet('admin/structure/types/manage/forum');
$this->submitForm(['options[promote]' => 'promote'], 'Save');
$this->createForumTopic($this->forum, FALSE);
$this->createForumTopic($this->forum, FALSE);
$this->drupalGet('node');
// Test adding a comment to a forum topic.
$node = $this->createForumTopic($this->forum, FALSE);
$edit = [];
$edit['comment_body[0][value]'] = $this->randomMachineName();
$this->drupalGet('node/' . $node->id());
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
// Test editing a forum topic that has a comment.
$this->drupalLogin($this->editAnyTopicsUser);
$this->drupalGet('forum/' . $this->forum['tid']);
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm([], 'Save');
$this->assertSession()->statusCodeEquals(200);
// Test the root forum page title change.
$this->drupalGet('forum');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:taxonomy.vocabulary.' . $this->forum['vid']);
$this->assertSession()->titleEquals('Forums | Drupal');
$vocabulary = Vocabulary::load($this->forum['vid']);
$vocabulary->set('name', 'Discussions');
$vocabulary->save();
$this->drupalGet('forum');
$this->assertSession()->titleEquals('Discussions | Drupal');
// Test anonymous action link.
$this->drupalLogout();
$this->drupalGet('forum/' . $this->forum['tid']);
$this->assertSession()->linkExists('Log in to post new content in the forum.');
}
/**
* Tests that forum nodes can't be added without a parent.
*
* Verifies that forum nodes are not created without choosing "forum" from the
* select list.
*/
public function testAddOrphanTopic(): void {
// Must remove forum topics to test creating orphan topics.
$vid = $this->config('forum.settings')->get('vocabulary');
$tids = \Drupal::entityQuery('taxonomy_term')
->accessCheck(FALSE)
->condition('vid', $vid)
->execute();
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $term_storage->loadMultiple($tids);
$term_storage->delete($terms);
// Create an orphan forum item.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(10);
$edit['body[0][value]'] = $this->randomMachineName(120);
$this->drupalLogin($this->adminUser);
$this->drupalGet('node/add/forum');
$this->submitForm($edit, 'Save');
$nid_count = $this->container->get('entity_type.manager')
->getStorage('node')
->getQuery()
->accessCheck(FALSE)
->count()
->execute();
$this->assertEquals(0, $nid_count, 'A forum node was not created when missing a forum vocabulary.');
// Reset the defaults for future tests.
\Drupal::service('module_installer')->install(['forum']);
}
/**
* Runs admin tests on the admin user.
*
* @param object $user
* The logged-in user.
*/
private function doAdminTests($user) {
// Log in the user.
$this->drupalLogin($user);
// Add forum to the Tools menu.
$edit = [];
$this->drupalGet('admin/structure/menu/manage/tools');
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
// Edit forum taxonomy.
// Restoration of the settings fails and causes subsequent tests to fail.
$this->editForumVocabulary();
// Create forum container.
$this->forumContainer = $this->createForum('container');
// Verify "edit container" link exists and functions correctly.
$this->drupalGet('admin/structure/forum');
// Verify help text is shown.
$this->assertSession()->pageTextContains('Forums contain forum topics. Use containers to group related forums');
// Verify action links are there.
$this->assertSession()->linkExists('Add forum');
$this->assertSession()->linkExists('Add container');
$this->clickLink('edit container');
$this->assertSession()->pageTextContains('Edit container');
// Create forum inside the forum container.
$this->forum = $this->createForum('forum', $this->forumContainer['tid']);
// Verify the "edit forum" link exists and functions correctly.
$this->drupalGet('admin/structure/forum');
$this->clickLink('edit forum');
$this->assertSession()->pageTextContains('Edit forum');
// Navigate back to forum structure page.
$this->drupalGet('admin/structure/forum');
// Create second forum in container, destined to be deleted below.
$delete_forum = $this->createForum('forum', $this->forumContainer['tid']);
// Save forum overview.
$this->drupalGet('admin/structure/forum/');
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains('The configuration options have been saved.');
// Delete this second forum.
$this->deleteForum($delete_forum['tid']);
// Create forum at the top (root) level.
$this->rootForum = $this->createForum('forum');
// Test vocabulary form alterations.
$this->drupalGet('admin/structure/taxonomy/manage/forums');
$this->assertSession()->buttonExists('Save');
$this->assertSession()->buttonNotExists('Delete');
// Test term edit form alterations.
$this->drupalGet('taxonomy/term/' . $this->forumContainer['tid'] . '/edit');
// Test parent field been hidden by forum module.
$this->assertSession()->fieldNotExists('parent[]');
// Create a default vocabulary named "Tags".
$description = 'Use tags to group articles on similar topics into categories.';
$help = 'Enter a comma-separated list of words to describe your content.';
$vocabulary = Vocabulary::create([
'name' => 'Tags',
'description' => $description,
'vid' => 'tags',
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
'help' => $help,
]);
$vocabulary->save();
// Test tags vocabulary form is not affected.
$this->drupalGet('admin/structure/taxonomy/manage/tags');
$this->assertSession()->buttonExists('Save');
$this->assertSession()->linkExists('Delete');
// Test tags vocabulary term form is not affected.
$this->drupalGet('admin/structure/taxonomy/manage/tags/add');
$this->assertSession()->fieldExists('parent[]');
// Test relations widget exists.
$this->assertSession()->elementExists('xpath', "//details[@id='edit-relations']");
}
/**
* Edits the forum taxonomy.
*/
public function editForumVocabulary() {
// Backup forum taxonomy.
$vid = $this->config('forum.settings')->get('vocabulary');
$original_vocabulary = Vocabulary::load($vid);
// Generate a random name and description.
$edit = [
'name' => $this->randomMachineName(10),
'description' => $this->randomMachineName(100),
];
// Edit the vocabulary.
$this->drupalGet('admin/structure/taxonomy/manage/' . $original_vocabulary->id());
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains("Updated vocabulary {$edit['name']}.");
// Grab the newly edited vocabulary.
$current_vocabulary = Vocabulary::load($vid);
// Make sure we actually edited the vocabulary properly.
$this->assertEquals($edit['name'], $current_vocabulary->label(), 'The name was updated');
$this->assertEquals($edit['description'], $current_vocabulary->getDescription(), 'The description was updated');
// Restore the original vocabulary's name and description.
$current_vocabulary->set('name', $original_vocabulary->label());
$current_vocabulary->set('description', $original_vocabulary->getDescription());
$current_vocabulary->save();
// Reload vocabulary to make sure changes are saved.
$current_vocabulary = Vocabulary::load($vid);
$this->assertEquals($original_vocabulary->label(), $current_vocabulary->label(), 'The original vocabulary settings were restored');
}
/**
* Creates a forum container or a forum.
*
* @param string $type
* The forum type (forum container or forum).
* @param int $parent
* The forum parent. This defaults to 0, indicating a root forum.
*
* @return \Drupal\Core\Database\StatementInterface
* The created taxonomy term data.
*/
public function createForum($type, $parent = 0) {
// Generate a random name/description.
$name = $this->randomMachineName(10);
$description = $this->randomMachineName(100);
$edit = [
'name[0][value]' => $name,
'description[0][value]' => $description,
'parent[0]' => $parent,
'weight' => '0',
];
// Create forum.
$this->drupalGet('admin/structure/forum/add/' . $type);
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
$type = ($type == 'container') ? 'forum container' : 'forum';
$this->assertSession()->pageTextContains('Created new ' . $type . ' ' . $name . '.');
// Verify that the creation message contains a link to a term.
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "term/")]');
/** @var \Drupal\taxonomy\TermStorageInterface $taxonomy_term_storage */
$taxonomy_term_storage = $this->container->get('entity_type.manager')->getStorage('taxonomy_term');
// Verify forum.
$term = $taxonomy_term_storage->loadByProperties([
'vid' => $this->config('forum.settings')->get('vocabulary'),
'name' => $name,
'description__value' => $description,
]);
$term = array_shift($term);
$this->assertNotEmpty($term, "The forum type '$type' should exist in the database.");
// Verify forum hierarchy.
$tid = $term->id();
$parent_tid = $taxonomy_term_storage->loadParents($tid);
$parent_tid = empty($parent_tid) ? 0 : array_shift($parent_tid)->id();
$this->assertSame($parent, $parent_tid, 'The ' . $type . ' is linked to its container');
$forum = $taxonomy_term_storage->load($tid);
$this->assertEquals(($type == 'forum container'), (bool) $forum->forum_container->value);
return [
'tid' => $tid,
'name' => $term->getName(),
'vid' => $term->bundle(),
];
}
/**
* Deletes a forum.
*
* @param int $tid
* The forum ID.
*/
public function deleteForum($tid) {
// Delete the forum.
$this->drupalGet('admin/structure/forum/edit/forum/' . $tid);
$this->clickLink('Delete');
$this->assertSession()->pageTextContains('Are you sure you want to delete the forum');
$this->assertSession()->pageTextNotContains('Add forum');
$this->assertSession()->pageTextNotContains('Add forum container');
$this->submitForm([], 'Delete');
// Assert that the forum no longer exists.
$this->drupalGet('forum/' . $tid);
$this->assertSession()->statusCodeEquals(404);
}
/**
* Runs basic tests on the indicated user.
*
* @param \Drupal\Core\Session\AccountInterface $user
* The logged in user.
* @param bool $admin
* User has 'access administration pages' privilege.
*/
private function doBasicTests($user, $admin) {
// Log in the user.
$this->drupalLogin($user);
// Attempt to create forum topic under a container.
$this->createForumTopic($this->forumContainer, TRUE);
// Create forum node.
$node = $this->createForumTopic($this->forum, FALSE);
// Verify the user has access to all the forum nodes.
$this->verifyForums($node, $admin);
}
/**
* Tests a forum with a new post displays properly.
*/
public function testForumWithNewPost(): void {
// Log in as the first user.
$this->drupalLogin($this->adminUser);
// Create a forum container.
$this->forumContainer = $this->createForum('container');
// Create a forum.
$this->forum = $this->createForum('forum');
// Create a topic.
$node = $this->createForumTopic($this->forum, FALSE);
// Log in as a second user.
$this->drupalLogin($this->postCommentUser);
// Post a reply to the topic.
$edit = [];
$edit['subject[0][value]'] = $this->randomMachineName();
$edit['comment_body[0][value]'] = $this->randomMachineName();
$this->drupalGet('node/' . $node->id());
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
// Test adding a new comment.
$this->clickLink('Add new comment');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->fieldExists('comment_body[0][value]');
// Log in as the first user.
$this->drupalLogin($this->adminUser);
// Check that forum renders properly.
$this->drupalGet("forum/{$this->forum['tid']}");
$this->assertSession()->statusCodeEquals(200);
// Verify there is no unintentional HTML tag escaping.
$this->assertSession()->assertNoEscaped('<');
}
/**
* Creates a forum topic.
*
* @param array $forum
* A forum array.
* @param bool $container
* TRUE if $forum is a container; FALSE otherwise.
*
* @return object|null
* The created topic node or NULL if the forum is a container.
*/
public function createForumTopic($forum, $container = FALSE) {
// Generate a random subject/body.
$title = $this->randomMachineName(20);
$body = $this->randomMachineName(200);
$edit = [
'title[0][value]' => $title,
'body[0][value]' => $body,
];
$tid = $forum['tid'];
// Create the forum topic, preselecting the forum ID via a URL parameter.
$this->drupalGet('node/add/forum', ['query' => ['forum_id' => $tid]]);
$this->submitForm($edit, 'Save');
if ($container) {
$this->assertSession()->pageTextNotContains("Forum topic $title has been created.");
$this->assertSession()->pageTextContains("The item {$forum['name']} is a forum container, not a forum.");
return;
}
else {
$this->assertSession()->pageTextContains("Forum topic $title has been created.");
$this->assertSession()->pageTextNotContains("The item {$forum['name']} is a forum container, not a forum.");
// Verify that the creation message contains a link to a node.
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "node/")]');
}
// Retrieve node object, ensure that the topic was created and in the proper forum.
$node = $this->drupalGetNodeByTitle($title);
$this->assertNotNull($node, "Node $title was loaded");
$this->assertEquals($tid, $node->taxonomy_forums->target_id, 'Saved forum topic was in the expected forum');
// View forum topic.
$this->drupalGet('node/' . $node->id());
$this->assertSession()->pageTextContains($title);
$this->assertSession()->pageTextContains($body);
return $node;
}
/**
* Verifies that the logged in user has access to a forum node.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The node being checked.
* @param bool $admin
* Boolean to indicate whether the user can 'access administration pages'.
* @param int $response
* The expected HTTP response code.
*/
private function verifyForums(EntityInterface $node, $admin, $response = 200) {
$response2 = ($admin) ? 200 : 403;
// View forum help node.
$this->drupalGet('admin/help/forum');
$this->assertSession()->statusCodeEquals($response2);
if ($response2 == 200) {
$this->assertSession()->titleEquals('Forum | Drupal');
$this->assertSession()->pageTextContains('Forum');
}
// View forum container page.
$this->verifyForumView($this->forumContainer);
// View forum page.
$this->verifyForumView($this->forum, $this->forumContainer);
// View root forum page.
$this->verifyForumView($this->rootForum);
// View forum node.
$this->drupalGet('node/' . $node->id());
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->titleEquals($node->label() . ' | Drupal');
$breadcrumb_build = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Forums', 'forum.index'),
Link::createFromRoute($this->forumContainer['name'], 'forum.page', ['taxonomy_term' => $this->forumContainer['tid']]),
Link::createFromRoute($this->forum['name'], 'forum.page', ['taxonomy_term' => $this->forum['tid']]),
];
$breadcrumb = [
'#theme' => 'breadcrumb',
'#links' => $breadcrumb_build,
];
$this->assertSession()->responseContains(\Drupal::service('renderer')->renderRoot($breadcrumb));
// View forum edit node.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertSession()->statusCodeEquals($response);
if ($response == 200) {
$this->assertSession()->titleEquals('Edit Forum topic ' . $node->label() . ' | Drupal');
}
if ($response == 200) {
// Edit forum node (including moving it to another forum).
$edit = [];
$edit['title[0][value]'] = 'node/' . $node->id();
$edit['body[0][value]'] = $this->randomMachineName(256);
// Assume the topic is initially associated with $forum.
$edit['taxonomy_forums'] = $this->rootForum['tid'];
$edit['shadow'] = TRUE;
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Forum topic ' . $edit['title[0][value]'] . ' has been updated.');
// Verify topic was moved to a different forum.
$forum_tid = $this->container
->get('database')
->select('forum', 'f')
->fields('f', ['tid'])
->condition('nid', $node->id())
->condition('vid', $node->getRevisionId())
->execute()
->fetchField();
$this->assertSame($this->rootForum['tid'], $forum_tid, 'The forum topic is linked to a different forum');
// Delete forum node.
$this->drupalGet('node/' . $node->id() . '/delete');
$this->submitForm([], 'Delete');
$this->assertSession()->statusCodeEquals($response);
$this->assertSession()->pageTextContains("Forum topic {$edit['title[0][value]']} has been deleted.");
}
}
/**
* Verifies the display of a forum page.
*
* @param array $forum
* A row from the taxonomy_term_data table in an array.
* @param array $parent
* (optional) An array representing the forum's parent.
*/
private function verifyForumView($forum, $parent = NULL) {
// View forum page.
$this->drupalGet('forum/' . $forum['tid']);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->titleEquals($forum['name'] . ' | Drupal');
$breadcrumb_build = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Forums', 'forum.index'),
];
if (isset($parent)) {
$breadcrumb_build[] = Link::createFromRoute($parent['name'], 'forum.page', ['taxonomy_term' => $parent['tid']]);
}
$breadcrumb = [
'#theme' => 'breadcrumb',
'#links' => $breadcrumb_build,
];
$this->assertSession()->responseContains(\Drupal::service('renderer')->renderRoot($breadcrumb));
}
/**
* Generates forum topics.
*/
private function generateForumTopics() {
$this->nids = [];
for ($i = 0; $i < 5; $i++) {
$node = $this->createForumTopic($this->forum, FALSE);
$this->nids[] = $node->id();
}
}
/**
* Evaluate whether "Add new Forum topic" button is present or not.
*/
public function testForumTopicButton(): void {
$this->drupalLogin($this->adminUser);
// Validate that link doesn't exist on the forum container page.
$forum_container = $this->createForum('container');
$this->drupalGet('forum/' . $forum_container['tid']);
$this->assertSession()->linkNotExists('Add new Forum topic');
// Validate that link exists on forum page.
$forum = $this->createForum('forum');
$this->drupalGet('forum/' . $forum['tid']);
$this->assertSession()->linkExists('Add new Forum topic');
}
}

View File

@@ -0,0 +1,189 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\comment\CommentInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\NodeType;
use Drupal\comment\Entity\Comment;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\BrowserTestBase;
/**
* Tests forum module uninstallation.
*
* @group forum
* @group legacy
* @group #slow
*/
class ForumUninstallTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['forum'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests if forum module uninstallation properly deletes the field.
*/
public function testForumUninstallWithField(): void {
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'administer nodes',
'administer modules',
'delete any forum content',
'administer content types',
]));
// Ensure that the field exists before uninstallation.
$field_storage = FieldStorageConfig::loadByName('node', 'taxonomy_forums');
$this->assertNotNull($field_storage, 'The taxonomy_forums field storage exists.');
// Create a taxonomy term.
$term = Term::create([
'name' => 'A term',
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
'description' => '',
'parent' => [0],
'vid' => 'forums',
'forum_container' => 0,
]);
$term->save();
// Create a forum node.
$node = $this->drupalCreateNode([
'title' => 'A forum post',
'type' => 'forum',
'taxonomy_forums' => [['target_id' => $term->id()]],
]);
// Create at least one comment against the forum node.
$comment = Comment::create([
'entity_id' => $node->nid->value,
'entity_type' => 'node',
'field_name' => 'comment_forum',
'pid' => 0,
'uid' => 0,
'status' => CommentInterface::PUBLISHED,
'subject' => $this->randomMachineName(),
'hostname' => '127.0.0.1',
]);
$comment->save();
// Attempt to uninstall forum.
$this->drupalGet('admin/modules/uninstall');
// Assert forum is required.
$this->assertSession()->fieldDisabled('uninstall[forum]');
$this->assertSession()->pageTextContains('To uninstall Forum, first delete all Forum content');
// Delete the node.
$this->drupalGet('node/' . $node->id() . '/delete');
$this->submitForm([], 'Delete');
// Attempt to uninstall forum.
$this->drupalGet('admin/modules/uninstall');
// Assert forum is still required.
$this->assertSession()->fieldDisabled('uninstall[forum]');
$this->assertSession()->pageTextContains('To uninstall Forum, first delete all Forums terms');
// Delete any forum terms.
$vid = $this->config('forum.settings')->get('vocabulary');
$storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $storage->loadByProperties(['vid' => $vid]);
$storage->delete($terms);
// Ensure that the forum node type can not be deleted.
$this->drupalGet('admin/structure/types/manage/forum');
$this->assertSession()->linkNotExists('Delete');
// Now attempt to uninstall forum.
$this->drupalGet('admin/modules/uninstall');
// Assert forum is no longer required.
$this->assertSession()->fieldExists('uninstall[forum]');
$this->drupalGet('admin/modules/uninstall');
$this->submitForm(['uninstall[forum]' => 1], 'Uninstall');
$this->submitForm([], 'Uninstall');
// Check that the field is now deleted.
$field_storage = FieldStorageConfig::loadByName('node', 'taxonomy_forums');
$this->assertNull($field_storage, 'The taxonomy_forums field storage has been deleted.');
// Check that a node type with a machine name of forum can be created after
// uninstalling the forum module and the node type is not locked.
$edit = [
'name' => 'Forum',
'title_label' => 'title for forum',
'type' => 'forum',
];
$this->drupalGet('admin/structure/types/add');
$this->submitForm($edit, 'Save');
$this->assertTrue((bool) NodeType::load('forum'), 'Node type with machine forum created.');
$this->drupalGet('admin/structure/types/manage/forum');
$this->clickLink('Delete');
$this->submitForm([], 'Delete');
$this->assertSession()->statusCodeEquals(200);
$this->assertFalse((bool) NodeType::load('forum'), 'Node type with machine forum deleted.');
// Double check everything by reinstalling the forum module again.
$this->drupalGet('admin/modules');
$this->submitForm(['modules[forum][enable]' => 1], 'Install');
$this->submitForm([], 'Continue');
$this->assertSession()->pageTextContains('Module Forum has been installed.');
}
/**
* Tests uninstallation if the field storage has been deleted beforehand.
*/
public function testForumUninstallWithoutFieldStorage(): void {
// Manually delete the taxonomy_forums field before module uninstallation.
$field_storage = FieldStorageConfig::loadByName('node', 'taxonomy_forums');
$this->assertNotNull($field_storage, 'The taxonomy_forums field storage exists.');
$field_storage->delete();
// Check that the field is now deleted.
$field_storage = FieldStorageConfig::loadByName('node', 'taxonomy_forums');
$this->assertNull($field_storage, 'The taxonomy_forums field storage has been deleted.');
// Delete all terms in the Forums vocabulary. Uninstalling the forum module
// will fail unless this is done.
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['vid' => 'forums']);
foreach ($terms as $term) {
$term->delete();
}
// Ensure that uninstallation succeeds even if the field has already been
// deleted manually beforehand.
$this->container->get('module_installer')->uninstall(['forum']);
}
/**
* Tests uninstallation of forum module when vocabulary is deleted.
*/
public function testForumUninstallWithoutForumVocabulary(): void {
$this->drupalLogin($this->drupalCreateUser([
'administer modules',
]));
Vocabulary::load('forums')->delete();
// Now attempt to uninstall forum.
$this->drupalGet('admin/modules/uninstall');
$this->assertSession()->responseNotContains('The website encountered an unexpected error. Try again later');
$this->assertSession()->statusCodeEquals(200);
// Assert forum is no longer required.
$this->assertSession()->fieldExists('uninstall[forum]');
$this->drupalGet('admin/modules/uninstall');
$this->submitForm(['uninstall[forum]' => 1], 'Uninstall');
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
/**
* Generic module test for forum.
*
* @group forum
* @group legacy
*/
class GenericTest extends GenericModuleTestBase {
/**
* {@inheritdoc}
*/
protected function preUninstallSteps(): void {
$storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $storage->loadMultiple();
$storage->delete($terms);
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional\Module;
use Drupal\Tests\system\Functional\Module\ModuleTestBase;
/**
* Enable module without dependency enabled.
*
* @group form
* @group legacy
*/
class DependencyTest extends ModuleTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests attempting to uninstall a module that has installed dependents.
*/
public function testUninstallDependents(): void {
// Enable the forum module.
$edit = ['modules[forum][enable]' => 'forum'];
$this->drupalGet('admin/modules');
$this->submitForm($edit, 'Install');
$this->submitForm([], 'Continue');
$this->assertModules(['forum'], TRUE);
// Check that the comment module cannot be uninstalled.
$this->drupalGet('admin/modules/uninstall');
$this->assertSession()->fieldDisabled('uninstall[comment]');
// Delete any forum terms.
$vid = $this->config('forum.settings')->get('vocabulary');
// Ensure taxonomy has been loaded into the test-runner after forum was
// enabled.
\Drupal::moduleHandler()->load('taxonomy');
$storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $storage->loadByProperties(['vid' => $vid]);
$storage->delete($terms);
// Uninstall the forum module, and check that taxonomy now can also be
// uninstalled.
$edit = ['uninstall[forum]' => 'forum'];
$this->drupalGet('admin/modules/uninstall');
$this->submitForm($edit, 'Uninstall');
$this->submitForm([], 'Uninstall');
$this->assertSession()->pageTextContains('The selected modules have been uninstalled.');
// Uninstall comment module.
$edit = ['uninstall[comment]' => 'comment'];
$this->drupalGet('admin/modules/uninstall');
$this->submitForm($edit, 'Uninstall');
$this->submitForm([], 'Uninstall');
$this->assertSession()->pageTextContains('The selected modules have been uninstalled.');
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests access controlled node views have the right amount of comment pages.
*
* @group form
* @group legacy
*/
class NodeAccessPagerTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['node_access_test', 'forum'];
/**
* A user account to use for the test.
*
* @var \Drupal\user\Entity\User
*/
protected $webUser;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
protected function setUp(): void {
parent::setUp();
node_access_rebuild();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$this->webUser = $this->drupalCreateUser([
'access content',
'node test view',
]);
}
/**
* Tests the forum node pager for nodes with multiple grants per realm.
*/
public function testForumPager(): void {
// Look up the forums vocabulary ID.
$vid = $this->config('forum.settings')->get('vocabulary');
$this->assertNotEmpty($vid, 'Forum navigation vocabulary ID is set.');
// Look up the general discussion term.
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, 0, 1);
$tid = reset($tree)->tid;
$this->assertNotEmpty($tid, 'General discussion term is found in the forum vocabulary.');
// Create 30 nodes.
for ($i = 0; $i < 30; $i++) {
$this->drupalCreateNode([
'nid' => NULL,
'type' => 'forum',
'taxonomy_forums' => [
['target_id' => $tid],
],
]);
}
// View the general discussion forum page. With the default 25 nodes per
// page there should be two pages for 30 nodes, no more.
$this->drupalLogin($this->webUser);
$this->drupalGet('forum/' . $tid);
$this->assertSession()->responseContains('page=1');
$this->assertSession()->responseNotContains('page=2');
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional;
use Drupal\Core\Database\Database;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Term;
/**
* Tests altering the inbound path and the outbound path.
*
* @group form
* @group legacy
*/
class UrlAlterFunctionalTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['path', 'forum', 'forum_url_alter_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests that URL altering works and that it occurs in the correct order.
*/
public function testUrlAlter(): void {
// Ensure that the path_alias table exists after Drupal installation.
$this->assertTrue(Database::getConnection()->schema()->tableExists('path_alias'), 'The path_alias table exists after Drupal installation.');
// Test that 'forum' is altered to 'community' correctly, both at the root
// level and for a specific existing forum.
$this->drupalGet('community');
$this->assertSession()->pageTextContains('General discussion');
$this->assertUrlOutboundAlter('/forum', '/community');
$forum_vid = $this->config('forum.settings')->get('vocabulary');
$term_name = $this->randomMachineName();
$term = Term::create([
'name' => $term_name,
'vid' => $forum_vid,
]);
$term->save();
$this->drupalGet("community/" . $term->id());
$this->assertSession()->pageTextContains($term_name);
$this->assertUrlOutboundAlter("/forum/" . $term->id(), "/community/" . $term->id());
}
/**
* Assert that an outbound path is altered to an expected value.
*
* @param string $original
* A string with the original path that is run through generateFrommPath().
* @param string $final
* A string with the expected result after generateFrommPath().
*
* @internal
*/
protected function assertUrlOutboundAlter(string $original, string $final): void {
// Test outbound altering.
$result = $this->container->get('path_processor_manager')->processOutbound($original);
$this->assertSame($final, $result, "Altered outbound URL $original, expected $final, and got $result.");
}
/**
* Assert that an inbound path is altered to an expected value.
*
* @param string $original
* The original path before it has been altered by inbound URL processing.
* @param string $final
* A string with the expected result.
*
* @internal
*/
protected function assertUrlInboundAlter(string $original, string $final): void {
// Test inbound altering.
$result = $this->container->get('path_alias.manager')->getPathByAlias($original);
$this->assertSame($final, $result, "Altered inbound URL $original, expected $final, and got $result.");
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional\Views;
use Drupal\node\NodeInterface;
use Drupal\views\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
/**
* Tests the forum integration into views.
*
* @group forum
* @group legacy
*/
class ForumIntegrationTest extends ViewTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['forum_test_views'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_forum_index'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = ['forum_test_views']): void {
parent::setUp($import_test_views, $modules);
}
/**
* Tests the integration.
*/
public function testForumIntegration(): void {
// Create a forum.
$entity_type_manager = $this->container->get('entity_type.manager');
$term = $entity_type_manager->getStorage('taxonomy_term')->create(['vid' => 'forums', 'name' => $this->randomMachineName()]);
$term->save();
$comment_storage = $entity_type_manager->getStorage('comment');
// Create some nodes which are part of this forum with some comments.
$nodes = [];
for ($i = 0; $i < 3; $i++) {
$node = $this->drupalCreateNode(['type' => 'forum', 'taxonomy_forums' => [$term->id()], 'sticky' => $i == 0 ? NodeInterface::STICKY : NodeInterface::NOT_STICKY]);
$nodes[] = $node;
}
$account = $this->drupalCreateUser(['skip comment approval']);
$this->drupalLogin($account);
$comments = [];
foreach ($nodes as $index => $node) {
for ($i = 0; $i <= $index; $i++) {
$comment = $comment_storage->create(['entity_type' => 'node', 'entity_id' => $node->id(), 'field_name' => 'comment_forum']);
$comment->save();
$comments[$comment->get('entity_id')->target_id][$comment->id()] = $comment;
}
}
$view = Views::getView('test_forum_index');
$this->executeView($view);
$expected_result = [];
$expected_result[] = [
'nid' => $nodes[0]->id(),
'sticky' => NodeInterface::STICKY,
'comment_count' => 1.,
];
$expected_result[] = [
'nid' => $nodes[1]->id(),
'sticky' => NodeInterface::NOT_STICKY,
'comment_count' => 2.,
];
$expected_result[] = [
'nid' => $nodes[2]->id(),
'sticky' => NodeInterface::NOT_STICKY,
'comment_count' => 3.,
];
$column_map = [
'nid' => 'nid',
'forum_index_sticky' => 'sticky',
'forum_index_comment_count' => 'comment_count',
];
$this->assertIdenticalResultset($view, $expected_result, $column_map);
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional\migrate_drupal\d6;
use Drupal\Tests\migrate_drupal_ui\Functional\NoMultilingualReviewPageTestBase;
/**
* Tests migrate upgrade review page.
*
* @group forum
* @group legacy
*/
class NoMultilingualReviewPageTest extends NoMultilingualReviewPageTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'forum',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->loadFixture($this->getModulePath('forum') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__;
}
/**
* Tests that Forum is displayed in the will be upgraded list.
*/
public function testMigrateUpgradeReviewPage(): void {
$this->prepare();
// Start the upgrade process.
$this->submitCredentialForm();
$session = $this->assertSession();
$this->submitForm([], 'I acknowledge I may lose data. Continue anyway.');
$session->statusCodeEquals(200);
// Confirm that Forum will be upgraded.
$session->elementExists('xpath', "//td[contains(@class, 'checked') and text() = 'Forum']");
$session->elementNotExists('xpath', "//td[contains(@class, 'error') and text() = 'Forum']");
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getIncompletePaths() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [];
}
}

View File

@@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional\migrate_drupal\d6;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase;
/**
* Tests Drupal 6 upgrade using the migrate UI.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group forum
* @group #slow
* @group legacy
*/
class Upgrade6Test extends MigrateUpgradeExecuteTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'forum',
'migrate_drupal_ui',
];
/**
* The entity storage for node.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $nodeStorage;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->loadFixture($this->getModulePath('forum') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'action' => 24,
'base_field_override' => 22,
'block' => 33,
'block_content' => 1,
'block_content_type' => 1,
'comment' => 4,
'comment_type' => 8,
'contact_form' => 2,
'contact_message' => 0,
'date_format' => 12,
'editor' => 2,
'entity_form_display' => 18,
'entity_form_mode' => 1,
'entity_view_display' => 34,
'entity_view_mode' => 11,
'field_config' => 41,
'field_storage_config' => 25,
'file' => 1,
'filter_format' => 7,
'image_style' => 6,
'menu' => 8,
'menu_link_content' => 1,
'node' => 3,
'node_type' => 7,
'path_alias' => 4,
'search_page' => 3,
'shortcut' => 2,
'shortcut_set' => 1,
'taxonomy_term' => 7,
'taxonomy_vocabulary' => 4,
'user' => 3,
'user_role' => 4,
'view' => 14,
];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'Block',
'Comment',
'Content',
'Date',
'Date API',
'Date Timezone',
'Email',
'Event',
'FileField',
'Filter',
'Forum',
'ImageAPI',
'ImageCache',
'ImageField',
'Menu',
'Node',
'Path',
'Search',
'System',
'Taxonomy',
'Text',
'Upload',
'User',
'Variable admin',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [];
}
/**
* Executes all steps of migrations upgrade.
*/
public function testUpgrade(): void {
// Start the upgrade process.
$this->submitCredentialForm();
$session = $this->assertSession();
$this->submitForm([], 'I acknowledge I may lose data. Continue anyway.');
$session->statusCodeEquals(200);
// Test the review form.
$this->assertReviewForm();
$this->submitForm([], 'Perform upgrade');
$this->assertUpgrade($this->getEntityCounts());
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional\migrate_drupal\d7;
use Drupal\Tests\migrate_drupal_ui\Functional\NoMultilingualReviewPageTestBase;
/**
* Tests Drupal 7 upgrade without translations.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group forum
* @group legacy
*/
class NoMultilingualReviewPageTest extends NoMultilingualReviewPageTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'forum',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->loadFixture($this->getModulePath('forum') . '/tests/fixtures/drupal7.php');
}
/**
* Tests that Forum is displayed in the will be upgraded list.
*/
public function testMigrateUpgradeReviewPage(): void {
$this->prepare();
// Start the upgrade process.
$this->submitCredentialForm();
$session = $this->assertSession();
$this->submitForm([], 'I acknowledge I may lose data. Continue anyway.');
$session->statusCodeEquals(200);
// Confirm that Forum will be upgraded.
$session->elementExists('xpath', "//td[contains(@class, 'checked') and text() = 'Forum']");
$session->elementNotExists('xpath', "//td[contains(@class, 'error') and text() = 'Forum']");
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getIncompletePaths() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [];
}
}

View File

@@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Functional\migrate_drupal\d7;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase;
/**
* Tests Drupal 7 upgrade using the migrate UI.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group forum
* @group #slow
* @group legacy
*/
class Upgrade7Test extends MigrateUpgradeExecuteTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'forum',
'migrate_drupal_ui',
];
/**
* The entity storage for node.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $nodeStorage;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// @todo remove in https://www.drupal.org/project/drupal/issues/3267040
// Delete the existing content made to test the ID Conflict form. Migrations
// are to be done on a site without content. The test of the ID Conflict
// form is being moved to its own issue which will remove the deletion
// of the created nodes.
// See https://www.drupal.org/project/drupal/issues/3087061.
$this->nodeStorage = $this->container->get('entity_type.manager')
->getStorage('node');
$this->nodeStorage->delete($this->nodeStorage->loadMultiple());
$this->loadFixture($this->getModulePath('forum') . '/tests/fixtures/drupal7.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'action' => 24,
'base_field_override' => 3,
'block' => 26,
'block_content' => 1,
'block_content_type' => 1,
'comment' => 0,
'comment_type' => 7,
'contact_form' => 2,
'contact_message' => 0,
'date_format' => 12,
'editor' => 2,
'entity_form_display' => 16,
'entity_form_mode' => 1,
'entity_view_display' => 24,
'entity_view_mode' => 11,
'field_config' => 26,
'field_storage_config' => 16,
'file' => 1,
'filter_format' => 7,
'image_style' => 7,
'menu' => 5,
'menu_link_content' => 3,
'node' => 2,
'node_type' => 6,
'path_alias' => 1,
'search_page' => 3,
'shortcut' => 2,
'shortcut_set' => 1,
'taxonomy_term' => 6,
'taxonomy_vocabulary' => 2,
'user' => 4,
'user_role' => 4,
'view' => 14,
];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'Block',
'Comment',
'Content translation',
'Date',
'Field SQL storage',
'Field',
'File',
'Filter',
'Forum',
'Image',
'Menu',
'Node',
'Options',
'Path',
'Search',
'System',
'Taxonomy',
'Text',
'User',
'Contextual links',
'Date API',
'Field UI',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'Locale',
'Entity Translation',
'Internationalization',
'String translation',
'Taxonomy translation',
'Translation sets',
'Variable',
];
}
/**
* Executes all steps of migrations upgrade.
*/
public function testUpgrade(): void {
// Start the upgrade process.
$this->submitCredentialForm();
$session = $this->assertSession();
$this->submitForm([], 'I acknowledge I may lose data. Continue anyway.');
$session->statusCodeEquals(200);
// Test the review form.
$this->assertReviewForm();
$this->submitForm([], 'Perform upgrade');
$this->assertUpgrade($this->getEntityCounts());
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel;
use Drupal\KernelTests\KernelTestBase;
/**
* Defines a class for testing the forum_index table.
*
* @group forum
*/
final class ForumIndexTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'user',
'node',
'history',
'taxonomy',
'forum',
'comment',
'options',
'text',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installEntitySchema('comment');
$this->installEntitySchema('taxonomy_term');
$this->installSchema('forum', ['forum_index']);
}
/**
* Tests there's a primary key on the forum_index table.
*/
public function testForumIndexIndex(): void {
$schema = \Drupal::database()->schema();
$this->assertTrue($schema->tableExists('forum_index'));
// We can't reliably call ::indexExists for each database driver as sqlite
// doesn't have named indexes for primary keys like mysql (PRIMARY) and
// pgsql (pkey).
$find_primary_key_columns = new \ReflectionMethod(get_class($schema), 'findPrimaryKeyColumns');
$this->assertEquals(['nid', 'tid'], $find_primary_key_columns->invoke($schema, 'forum_index'));
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term;
/**
* Tests forum validation constraints.
*
* @group forum
* @group legacy
*/
class ForumValidationTest extends EntityKernelTestBase {
/**
* Modules to install.
*
* @var array
*/
protected static $modules = [
'node',
'options',
'comment',
'taxonomy',
'forum',
];
/**
* Tests the forum validation constraints.
*/
public function testValidation(): void {
$this->installConfig('forum');
// Add a forum.
$forum = Term::create([
'name' => 'forum 1',
'vid' => 'forums',
'forum_container' => 0,
]);
// Add a container.
$container = Term::create([
'name' => 'container 1',
'vid' => 'forums',
'forum_container' => 1,
]);
// Add a forum post.
$forum_post = Node::create([
'type' => 'forum',
'title' => 'Do these pants make my butt look big?',
]);
$violations = $forum_post->validate();
$this->assertCount(1, $violations);
$this->assertEquals('This value should not be null.', $violations[0]->getMessage());
// Add the forum term.
$forum_post->set('taxonomy_forums', $forum);
$violations = $forum_post->validate();
$this->assertCount(0, $violations);
// Try to use a container.
$forum_post->set('taxonomy_forums', $container);
$violations = $forum_post->validate();
$this->assertCount(1, $violations);
$this->assertEquals(sprintf('The item %s is a forum container, not a forum. Select one of the forums below instead.', $container->label()), $violations[0]->getMessage());
}
}

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\TermInterface;
/**
* Common assertions for migration tests.
*/
trait MigrateTestTrait {
/**
* The cached taxonomy tree items, keyed by vid and tid.
*
* @var array
*/
protected $treeData = [];
/**
* Validate a migrated term contains the expected values.
*
* @param int $id
* Entity ID to load and check.
* @param string $expected_language
* The language code for this term.
* @param $expected_label
* The label the migrated entity should have.
* @param $expected_vid
* The parent vocabulary the migrated entity should have.
* @param string|null $expected_description
* The description the migrated entity should have.
* @param string|null $expected_format
* The format the migrated entity should have.
* @param int $expected_weight
* The weight the migrated entity should have.
* @param array $expected_parents
* The parent terms the migrated entity should have.
* @param int|null $expected_container_flag
* The term should be a container entity.
*
* @internal
*/
protected function assertEntity(int $id, string $expected_language, string $expected_label, string $expected_vid, ?string $expected_description = '', ?string $expected_format = NULL, int $expected_weight = 0, array $expected_parents = [], int|null $expected_container_flag = NULL): void {
/** @var \Drupal\taxonomy\TermInterface $entity */
$entity = Term::load($id);
$this->assertInstanceOf(TermInterface::class, $entity);
$this->assertSame($expected_language, $entity->language()->getId());
$this->assertEquals($expected_label, $entity->label());
$this->assertEquals($expected_vid, $entity->bundle());
$this->assertEquals($expected_description, $entity->getDescription());
$this->assertEquals($expected_format, $entity->getFormat());
$this->assertEquals($expected_weight, $entity->getWeight());
$this->assertEquals($expected_parents, array_column($entity->get('parent')
->getValue(), 'target_id'));
$this->assertHierarchy($expected_vid, $id, $expected_parents);
if (isset($expected_container_flag)) {
$this->assertEquals($expected_container_flag, $entity->forum_container->value);
}
}
/**
* Retrieves the parent term IDs for a given term.
*
* @param $tid
* ID of the term to check.
*
* @return array
* List of parent term IDs.
*/
protected function getParentIDs($tid) {
return array_keys(\Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadParents($tid));
}
/**
* Assert that a term is present in the tree storage, with the right parents.
*
* @param string $vid
* Vocabulary ID.
* @param int $tid
* ID of the term to check.
* @param array $parent_ids
* The expected parent term IDs.
*/
protected function assertHierarchy(string $vid, int $tid, array $parent_ids): void {
if (!isset($this->treeData[$vid])) {
$tree = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadTree($vid);
$this->treeData[$vid] = [];
foreach ($tree as $item) {
$this->treeData[$vid][$item->tid] = $item;
}
}
$this->assertArrayHasKey($tid, $this->treeData[$vid], "Term $tid exists in taxonomy tree");
$term = $this->treeData[$vid][$tid];
$this->assertEquals($parent_ids, $term->parents, "Term $tid has correct parents in taxonomy tree");
}
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\block\Entity\Block;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests migration of forum blocks.
*
* @group forum
*/
class MigrateBlockTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'block',
'block_content',
'comment',
'forum',
'node',
'path_alias',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('path_alias');
// Install the themes used for this test.
$this->installEntitySchema('block_content');
$this->container->get('theme_installer')->install(['olivero', 'test_theme']);
$this->installConfig(['block_content']);
// Set Olivero as the default public theme.
$config = $this->config('system.theme');
$config->set('default', 'olivero');
$config->save();
$this->executeMigrations([
'd6_filter_format',
'block_content_type',
'block_content_body_field',
'd6_custom_block',
'd6_user_role',
'd6_block',
]);
block_rebuild();
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Asserts various aspects of a block.
*
* @param string $id
* The block ID.
* @param array $visibility
* The block visibility settings.
* @param string $region
* The display region.
* @param string $theme
* The theme.
* @param int $weight
* The block weight.
* @param array $settings
* (optional) The block settings.
* @param bool $status
* Whether the block is expected to be enabled or disabled.
*
* @internal
*/
public function assertEntity(string $id, array $visibility, string $region, string $theme, int $weight, array $settings = [], bool $status = TRUE): void {
$block = Block::load($id);
$this->assertInstanceOf(Block::class, $block);
$this->assertSame($visibility, $block->getVisibility());
$this->assertSame($region, $block->getRegion());
$this->assertSame($theme, $block->getTheme());
$this->assertSame($weight, $block->getWeight());
$this->assertSame($status, $block->status());
if ($settings) {
$block_settings = $block->get('settings');
$block_settings['id'] = current(explode(':', $block_settings['id']));
$this->assertEquals($settings, $block_settings);
}
}
/**
* Tests the block migration.
*/
public function testBlockMigration(): void {
// Check forum block settings.
$settings = [
'id' => 'forum_active_block',
'label' => '',
'provider' => 'forum',
'label_display' => '0',
'block_count' => 3,
'properties' => [
'administrative' => '1',
],
];
$this->assertEntity('forum', [], 'sidebar', 'olivero', -8, $settings);
$settings = [
'id' => 'forum_new_block',
'label' => '',
'provider' => 'forum',
'label_display' => '0',
'block_count' => 4,
'properties' => [
'administrative' => '1',
],
];
$this->assertEntity('forum_1', [], 'sidebar', 'olivero', -9, $settings);
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to forum.settings.yml.
*
* @group migrate_drupal_6
* @group legacy
*/
class MigrateForumConfigsTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['comment', 'forum', 'taxonomy'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigration('d6_taxonomy_vocabulary');
$this->executeMigration('d6_forum_settings');
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Tests migration of forum variables to forum.settings.yml.
*/
public function testForumSettings(): void {
$config = $this->config('forum.settings');
$this->assertSame(15, $config->get('topics.hot_threshold'));
$this->assertSame(25, $config->get('topics.page_limit'));
$this->assertSame(1, $config->get('topics.order'));
$this->assertSame('forums', $config->get('vocabulary'));
// This is 'forum_block_num_0' in D6, but block:active:limit' in D8.
$this->assertSame(3, $config->get('block.active.limit'));
// This is 'forum_block_num_1' in D6, but 'block:new:limit' in D8.
$this->assertSame(4, $config->get('block.new.limit'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'forum.settings', $config->get());
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\Node;
use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase;
/**
* Tests forum migration from Drupal 6 to Drupal 8.
*
* @group migrate_drupal_6
* @group legacy
*/
class MigrateForumTest extends MigrateNodeTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
'menu_ui',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('comment');
$this->installSchema('comment', ['comment_entity_statistics']);
$this->installSchema('forum', ['forum', 'forum_index']);
$this->installConfig(['comment', 'forum']);
$this->migrateContent();
$this->migrateTaxonomy();
$this->executeMigrations([
'd6_comment_type',
'd6_comment_field',
'd6_comment_field_instance',
'd6_comment_entity_display',
'd6_comment_entity_form_display',
'd6_comment',
'd6_term_node',
]);
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Tests forum migration.
*/
public function testForumMigration(): void {
// Tests that the taxonomy_forums field storage config exists.
$field_storage_config = FieldStorageConfig::load('node.taxonomy_forums');
$this->assertInstanceOf(FieldStorageConfig::class, $field_storage_config);
// Tests that the taxonomy_forums field config exists.
$field_config = FieldConfig::load('node.forum.taxonomy_forums');
$this->assertInstanceOf(FieldConfig::class, $field_config);
// Tests that the taxonomy_forums entity view display component exists.
$entity_view_display = EntityViewDisplay::load('node.forum.default')->getComponent('taxonomy_forums');
$this->assertIsArray($entity_view_display);
// Tests that the taxonomy_forums entity form display component exists.
$entity_form_display = EntityFormDisplay::load('node.forum.default')->getComponent('taxonomy_forums');
$this->assertIsArray($entity_form_display);
// Test that the taxonomy_forums field has the right value.
$node = Node::load(19);
$this->assertEquals(8, $node->taxonomy_forums->target_id);
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Tests\forum\Kernel\Migrate\MigrateTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Test migration of forum taxonomy terms.
*
* @group forum
*/
class MigrateTaxonomyTermTest extends MigrateDrupal6TestBase {
use MigrateTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'comment', 'forum', 'menu_ui'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('taxonomy_term');
$this->installConfig('forum');
$this->executeMigrations(['d6_taxonomy_vocabulary', 'd6_taxonomy_term']);
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Assert the forum taxonomy terms.
*/
public function testTaxonomyTerms(): void {
$this->assertEntity(8, 'en', 'General discussion', 'forums', '', NULL, 2, ['0'], 0);
$this->assertEntity(9, 'en', 'Earth', 'forums', '', NULL, 0, ['0'], 1);
$this->assertEntity(10, 'en', 'Birds', 'forums', '', NULL, 0, ['9'], 0);
$this->assertEntity(11, 'en', 'Oak', 'trees', '', NULL, 0, ['0'], NULL);
$this->assertEntity(12, 'en', 'Ash', 'trees', '', NULL, 0, ['0'], NULL);
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Tests\taxonomy\Kernel\Migrate\d6\MigrateTaxonomyVocabularyTest as TaxonomyVocabularyTest;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\VocabularyInterface;
/**
* Migrate forum vocabulary to taxonomy.vocabulary.*.yml.
*
* @group forum
*/
class MigrateTaxonomyVocabularyTest extends TaxonomyVocabularyTest {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
];
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Validate a migrated vocabulary contains the expected values.
*
* @param string $id
* Entity ID to load and check.
* @param $expected_label
* The label the migrated entity should have.
* @param $expected_description
* The description the migrated entity should have.
* @param $expected_weight
* The weight the migrated entity should have.
*
* @internal
*/
protected function assertEntity(string $id, string $expected_label, string $expected_description, int $expected_weight): void {
/** @var \Drupal\taxonomy\VocabularyInterface $entity */
$entity = Vocabulary::load($id);
$this->assertInstanceOf(VocabularyInterface::class, $entity);
$this->assertSame($expected_label, $entity->label());
$this->assertSame($expected_description, $entity->getDescription());
$this->assertSame($expected_weight, (int) $entity->get('weight'));
}
/**
* Tests the Drupal 6 taxonomy vocabularies migration.
*/
public function testTaxonomyVocabulary(): void {
$this->assertEntity('forums', 'Forums', '', 0);
$this->assertEntity('trees', 'Trees', 'A list of trees.', 0);
$this->assertEntity('freetags', 'FreeTags', '', 0);
}
}

View File

@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Vocabulary entity display migration.
*
* @group forum
*/
class MigrateVocabularyEntityDisplayTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'field',
'comment',
'forum',
'taxonomy',
'menu_ui',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Execute Dependency Migrations.
$this->migrateContentTypes();
$this->installEntitySchema('taxonomy_term');
$this->executeMigrations([
'd6_node_type',
'd6_taxonomy_vocabulary',
'd6_vocabulary_field',
'd6_vocabulary_field_instance',
'd6_vocabulary_entity_display',
]);
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration.
*/
public function testVocabularyEntityDisplay(): void {
$this->assertEntity('node.forum.default');
$this->assertComponent('node.forum.default', 'taxonomy_forums', 'entity_reference_label', 'hidden', 20);
$this->assertComponent('node.forum.default', 'field_trees', 'entity_reference_label', 'hidden', 20);
$this->assertComponent('node.forum.default', 'field_freetags', 'entity_reference_label', 'hidden', 20);
}
/**
* Asserts various aspects of a view display.
*
* @param string $id
* The view display ID.
*
* @internal
*/
protected function assertEntity(string $id): void {
$display = EntityViewDisplay::load($id);
$this->assertInstanceOf(EntityViewDisplayInterface::class, $display);
}
/**
* Asserts various aspects of a particular component of a view display.
*
* @param string $display_id
* The view display ID.
* @param string $component_id
* The component ID.
* @param string $type
* The expected component type (formatter plugin ID).
* @param string $label
* The expected label of the component.
* @param int $weight
* The expected weight of the component.
*
* @internal
*/
protected function assertComponent(string $display_id, string $component_id, string $type, string $label, int $weight): void {
$component = EntityViewDisplay::load($display_id)->getComponent($component_id);
$this->assertIsArray($component);
$this->assertSame($type, $component['type']);
$this->assertSame($label, $component['label']);
$this->assertSame($weight, $component['weight']);
}
/**
* Asserts that a particular component is NOT included in a display.
*
* @param string $display_id
* The display ID.
* @param string $component_id
* The component ID.
*
* @internal
*/
protected function assertComponentNotExists(string $display_id, string $component_id): void {
$component = EntityViewDisplay::load($display_id)->getComponent($component_id);
$this->assertNull($component);
}
}

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Vocabulary entity form display migration.
*
* @group forum
*/
class MigrateVocabularyEntityFormDisplayTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['comment', 'forum', 'taxonomy', 'menu_ui'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Execute Dependency Migrations.
$this->migrateContentTypes();
$this->installEntitySchema('taxonomy_term');
$this->executeMigrations([
'd6_taxonomy_vocabulary',
'd6_vocabulary_field',
'd6_vocabulary_field_instance',
'd6_vocabulary_entity_display',
'd6_vocabulary_entity_form_display',
]);
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration.
*/
public function testVocabularyEntityFormDisplay(): void {
$this->assertEntity('node.forum.default', 'node', 'forum');
$this->assertComponent('node.forum.default', 'taxonomy_forums', 'options_select', 20);
$this->assertComponent('node.forum.default', 'field_trees', 'options_select', 20);
$this->assertComponent('node.forum.default', 'field_freetags', 'entity_reference_autocomplete_tags', 20);
}
/**
* Asserts various aspects of a form display entity.
*
* @param string $id
* The entity ID.
* @param string $expected_entity_type
* The expected entity type to which the display settings are attached.
* @param string $expected_bundle
* The expected bundle to which the display settings are attached.
*
* @internal
*/
protected function assertEntity(string $id, string $expected_entity_type, string $expected_bundle): void {
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity */
$entity = EntityFormDisplay::load($id);
$this->assertInstanceOf(EntityFormDisplayInterface::class, $entity);
$this->assertSame($expected_entity_type, $entity->getTargetEntityTypeId());
$this->assertSame($expected_bundle, $entity->getTargetBundle());
}
/**
* Asserts various aspects of a particular component of a form display.
*
* @param string $display_id
* The form display ID.
* @param string $component_id
* The component ID.
* @param string $widget_type
* The expected widget type.
* @param int $weight
* The expected weight of the component.
*
* @internal
*/
protected function assertComponent(string $display_id, string $component_id, string $widget_type, int $weight): void {
$component = EntityFormDisplay::load($display_id)->getComponent($component_id);
$this->assertIsArray($component);
$this->assertSame($widget_type, $component['type']);
$this->assertSame($weight, $component['weight']);
}
}

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\FieldConfigInterface;
/**
* Vocabulary field instance migration.
*
* @group forum
*/
class MigrateVocabularyFieldInstanceTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
'menu_ui',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Execute Dependency Migrations.
$this->migrateContentTypes();
$this->installEntitySchema('taxonomy_term');
$this->executeMigrations([
'd6_node_type',
'd6_taxonomy_vocabulary',
'd6_vocabulary_field',
'd6_vocabulary_field_instance',
]);
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Tests the Drupal 6 vocabulary-node type association migration.
*/
public function testVocabularyFieldInstance(): void {
$this->assertEntity('node.forum.taxonomy_forums', 'Forums', 'entity_reference', FALSE, FALSE);
$this->assertEntity('node.forum.field_trees', 'Trees', 'entity_reference', FALSE, FALSE);
$this->assertEntity('node.forum.field_freetags', 'FreeTags', 'entity_reference', FALSE, FALSE);
}
/**
* Asserts various aspects of a field config entity.
*
* @param string $id
* The entity ID in the form ENTITY_TYPE.BUNDLE.FIELD_NAME.
* @param string $expected_label
* The expected field label.
* @param string $expected_field_type
* The expected field type.
* @param bool $is_required
* Whether or not the field is required.
* @param bool $expected_translatable
* Whether or not the field is expected to be translatable.
*
* @internal
*/
protected function assertEntity(string $id, string $expected_label, string $expected_field_type, bool $is_required, bool $expected_translatable): void {
[$expected_entity_type, $expected_bundle, $expected_name] = explode('.', $id);
/** @var \Drupal\field\FieldConfigInterface $field */
$field = FieldConfig::load($id);
$this->assertInstanceOf(FieldConfigInterface::class, $field);
$this->assertEquals($expected_label, $field->label());
$this->assertEquals($expected_field_type, $field->getType());
$this->assertEquals($expected_entity_type, $field->getTargetEntityTypeId());
$this->assertEquals($expected_bundle, $field->getTargetBundle());
$this->assertEquals($expected_name, $field->getName());
$this->assertEquals($is_required, $field->isRequired());
$this->assertEquals($expected_entity_type . '.' . $expected_name, $field->getFieldStorageDefinition()->id());
$this->assertEquals($expected_translatable, $field->isTranslatable());
}
/**
* Asserts the settings of a link field config entity.
*
* @param string $id
* The entity ID in the form ENTITY_TYPE.BUNDLE.FIELD_NAME.
* @param int $title_setting
* The expected title setting.
*
* @internal
*/
protected function assertLinkFields(string $id, int $title_setting): void {
$field = FieldConfig::load($id);
$this->assertSame($title_setting, $field->getSetting('title'));
}
/**
* Asserts the settings of an entity reference field config entity.
*
* @param string $id
* The entity ID in the form ENTITY_TYPE.BUNDLE.FIELD_NAME.
* @param string[] $target_bundles
* An array of expected target bundles.
*
* @internal
*/
protected function assertEntityReferenceFields(string $id, array $target_bundles): void {
$field = FieldConfig::load($id);
$handler_settings = $field->getSetting('handler_settings');
$this->assertArrayHasKey('target_bundles', $handler_settings);
foreach ($handler_settings['target_bundles'] as $target_bundle) {
$this->assertContains($target_bundle, $target_bundles);
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\FieldStorageConfigInterface;
/**
* Vocabulary field migration.
*
* @group forum
*/
class MigrateVocabularyFieldTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
'taxonomy',
'menu_ui',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->migrateTaxonomy();
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal6.php';
}
/**
* Tests the Drupal 6 vocabulary-node type association migration.
*/
public function testVocabularyField(): void {
// Test that the field exists.
$this->assertEntity('node.field_freetags', 'entity_reference', TRUE, -1);
$this->assertEntity('node.field_trees', 'entity_reference', TRUE, 1);
$this->assertEntity('node.taxonomy_forums', 'entity_reference', TRUE, 1);
}
/**
* Asserts various aspects of a field_storage_config entity.
*
* @param string $id
* The entity ID in the form ENTITY_TYPE.FIELD_NAME.
* @param string $expected_type
* The expected field type.
* @param bool $expected_translatable
* Whether or not the field is expected to be translatable.
* @param int $expected_cardinality
* The expected cardinality of the field.
*
* @internal
*/
protected function assertEntity(string $id, string $expected_type, bool $expected_translatable, int $expected_cardinality): void {
[$expected_entity_type, $expected_name] = explode('.', $id);
/** @var \Drupal\field\FieldStorageConfigInterface $field */
$field = FieldStorageConfig::load($id);
$this->assertInstanceOf(FieldStorageConfigInterface::class, $field);
$this->assertEquals($expected_name, $field->getName());
$this->assertEquals($expected_type, $field->getType());
$this->assertEquals($expected_translatable, $field->isTranslatable());
$this->assertEquals($expected_entity_type, $field->getTargetEntityTypeId());
if ($expected_cardinality === 1) {
$this->assertFalse($field->isMultiple());
}
else {
$this->assertTrue($field->isMultiple());
}
$this->assertEquals($expected_cardinality, $field->getCardinality());
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of Forum's variables to configuration.
*
* @group forum
* @group legacy
*/
class MigrateForumSettingsTest extends MigrateDrupal7TestBase {
/**
* Modules to enable.
*
* Don't alphabetize these. They're in dependency order.
*
* @var array
*/
protected static $modules = [
'comment',
'field',
'filter',
'text',
'node',
'taxonomy',
'forum',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigration('d7_taxonomy_vocabulary');
$this->executeMigration('d7_forum_settings');
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal7.php';
}
/**
* Tests the migration of Forum's settings to configuration.
*/
public function testForumSettingsMigration(): void {
$config = $this->config('forum.settings');
$this->assertSame(9, $config->get('block.active.limit'));
$this->assertSame(4, $config->get('block.new.limit'));
$this->assertSame(10, $config->get('topics.hot_threshold'));
$this->assertSame(25, $config->get('topics.page_limit'));
$this->assertSame(1, $config->get('topics.order'));
$this->assertSame('forums', $config->get('vocabulary'));
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d7;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\language\Kernel\Migrate\d7\MigrateLanguageContentTaxonomyVocabularySettingsTest as CoreTest;
/**
* Tests migration of i18ntaxonomy vocabulary settings.
*
* @group forum
*/
class MigrateLanguageContentTaxonomyVocabularySettingsTest extends CoreTest {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
];
/**
* Tests migration of 18ntaxonomy vocabulary settings.
*/
public function testLanguageContentTaxonomy(): void {
$this->assertLanguageContentSettings('taxonomy_term', 'forums', LanguageInterface::LANGCODE_NOT_SPECIFIED, FALSE, ['enabled' => FALSE]);
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d7;
use Drupal\Tests\forum\Kernel\Migrate\MigrateTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\taxonomy\Entity\Term;
/**
* Test migration of forum taxonomy terms.
*
* @group forum
*/
class MigrateTaxonomyTermTest extends MigrateDrupal7TestBase {
use MigrateTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
'content_translation',
'datetime',
'datetime_range',
'image',
'language',
'menu_ui',
'node',
'taxonomy',
'text',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installConfig('forum');
$this->installEntitySchema('comment');
$this->installEntitySchema('file');
$this->migrateTaxonomyTerms();
$this->executeMigrations([
'language',
'd7_user_role',
'd7_user',
'd7_entity_translation_settings',
'd7_taxonomy_term_entity_translation',
]);
}
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal7.php';
}
/**
* Assert the forum taxonomy terms.
*/
public function testTaxonomyTerms(): void {
$this->assertEntity(1, 'en', 'General discussion', 'forums', '', NULL, 2, ['0'], 0);
$this->assertEntity(5, 'en', 'Custom Forum', 'forums', 'Where the cool kids are.', NULL, 3, ['0'], 0);
$this->assertEntity(6, 'en', 'Games', 'forums', NULL, '', 4, ['0'], 1);
$this->assertEntity(7, 'en', 'Minecraft', 'forums', '', NULL, 1, [6], 0);
$this->assertEntity(8, 'en', 'Half Life 3', 'forums', '', NULL, 0, [6], 0);
// Verify that we still can create forum containers after the migration.
$term = Term::create([
'vid' => 'forums',
'name' => 'Forum Container',
'forum_container' => 1,
]);
$term->save();
// Reset the forums tree data so this new term is included in the tree.
unset($this->treeData['forums']);
$this->assertEntity(9, 'en', 'Forum Container', 'forums', '', '', 0, ['0'], 1);
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d7;
use Drupal\Tests\taxonomy\Kernel\Migrate\d7\MigrateTaxonomyTermTranslationTest as TaxonomyTermTranslationTest;
/**
* Test migration of translated taxonomy terms.
*
* @group forum
*/
class MigrateTaxonomyTermTranslationTest extends TaxonomyTermTranslationTest {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
];
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal7.php';
}
/**
* Tests the Drupal i18n taxonomy term to Drupal 8 migration.
*/
public function testTaxonomyTermTranslation(): void {
// Forums vocabulary, no multilingual option.
$this->assertEntity(1, 'en', 'General discussion', 'forums', NULL, NULL, 2, []);
$this->assertEntity(5, 'en', 'Custom Forum', 'forums', 'Where the cool kids are.', NULL, 3, []);
$this->assertEntity(6, 'en', 'Games', 'forums', NULL, NULL, 4, []);
$this->assertEntity(7, 'en', 'Minecraft', 'forums', NULL, NULL, 1, ['6']);
$this->assertEntity(8, 'en', 'Half Life 3', 'forums', NULL, NULL, 0, ['6']);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Kernel\Migrate\d7;
use Drupal\Tests\taxonomy\Kernel\Migrate\d7\MigrateTaxonomyVocabularyTest as TaxonomyVocabularyTest;
/**
* Migrate forum vocabulary to taxonomy.vocabulary.*.yml.
*
* @group forum
*/
class MigrateTaxonomyVocabularyTest extends TaxonomyVocabularyTest {
/**
* {@inheritdoc}
*/
protected static $modules = [
'comment',
'forum',
];
/**
* Gets the path to the fixture file.
*/
protected function getFixtureFilePath() {
return __DIR__ . '/../../../../fixtures/drupal7.php';
}
/**
* Tests the Drupal 7 taxonomy vocabularies to Drupal 8 migration.
*/
public function testTaxonomyVocabulary(): void {
$this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', 0);
$this->assertEntity('forums', 'Subject of discussion', 'Forum navigation vocabulary', -10);
}
}

View File

@@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Unit\Breadcrumb;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\Container;
/**
* @coversDefaultClass \Drupal\forum\Breadcrumb\ForumBreadcrumbBuilderBase
* @group forum
* @group legacy
*/
class ForumBreadcrumbBuilderBaseTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
$cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
$container = new Container();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
}
/**
* Tests ForumBreadcrumbBuilderBase::__construct().
*
* @covers ::__construct
*/
public function testConstructor(): void {
// Make some test doubles.
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$config_factory = $this->getConfigFactoryStub(
[
'forum.settings' => ['IAmATestKey' => 'IAmATestValue'],
]
);
$forum_manager = $this->createMock('Drupal\forum\ForumManagerInterface');
$translation_manager = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface');
// Make an object to test.
$builder = $this->getMockForAbstractClass(
'Drupal\forum\Breadcrumb\ForumBreadcrumbBuilderBase',
// Constructor array.
[
$entity_type_manager,
$config_factory,
$forum_manager,
$translation_manager,
]
);
// Test that the constructor made a config object with our info in it.
$reflector = new \ReflectionClass($builder);
$ref_property = $reflector->getProperty('config');
$config = $ref_property->getValue($builder);
$this->assertEquals('IAmATestValue', $config->get('IAmATestKey'));
}
/**
* Tests ForumBreadcrumbBuilderBase::build().
*
* @see \Drupal\forum\Breadcrumb\ForumBreadcrumbBuilderBase::build()
*
* @covers ::build
*/
public function testBuild(): void {
// Build all our dependencies, backwards.
$translation_manager = $this->getMockBuilder('Drupal\Core\StringTranslation\TranslationInterface')
->disableOriginalConstructor()
->getMock();
$forum_manager = $this->getMockBuilder('Drupal\forum\ForumManagerInterface')
->disableOriginalConstructor()
->getMock();
$prophecy = $this->prophesize('Drupal\taxonomy\VocabularyInterface');
$prophecy->label()->willReturn('Fora_is_the_plural_of_forum');
$prophecy->id()->willReturn(5);
$prophecy->getCacheTags()->willReturn(['taxonomy_vocabulary:5']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$vocab_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
$vocab_storage->expects($this->any())
->method('load')
->willReturnMap([
['forums', $prophecy->reveal()],
]);
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$entity_type_manager->expects($this->any())
->method('getStorage')
->willReturnMap([
['taxonomy_vocabulary', $vocab_storage],
]);
$config_factory = $this->getConfigFactoryStub(
[
'forum.settings' => [
'vocabulary' => 'forums',
],
]
);
// Build a breadcrumb builder to test.
$breadcrumb_builder = $this->getMockForAbstractClass(
'Drupal\forum\Breadcrumb\ForumBreadcrumbBuilderBase',
// Constructor array.
[
$entity_type_manager,
$config_factory,
$forum_manager,
$translation_manager,
]
);
// Add a translation manager for t().
$translation_manager = $this->getStringTranslationStub();
$breadcrumb_builder->setStringTranslation($translation_manager);
// Our empty data set.
$route_match = $this->createMock('Drupal\Core\Routing\RouteMatchInterface');
// Expected result set.
$expected = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Fora_is_the_plural_of_forum', 'forum.index'),
];
// And finally, the test.
$breadcrumb = $breadcrumb_builder->build($route_match);
$this->assertEquals($expected, $breadcrumb->getLinks());
$this->assertEquals(['route'], $breadcrumb->getCacheContexts());
$this->assertEquals(['taxonomy_vocabulary:5'], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
}

View File

@@ -0,0 +1,218 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Unit\Breadcrumb;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link;
use Drupal\forum\Breadcrumb\ForumListingBreadcrumbBuilder;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\TermStorageInterface;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\Container;
/**
* @coversDefaultClass \Drupal\forum\Breadcrumb\ForumListingBreadcrumbBuilder
* @group forum
* @group legacy
*/
class ForumListingBreadcrumbBuilderTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
$cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
$container = new Container();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
}
/**
* Tests ForumListingBreadcrumbBuilder::applies().
*
* @param bool $expected
* ForumListingBreadcrumbBuilder::applies() expected result.
* @param string|null $route_name
* (optional) A route name.
* @param array $parameter_map
* (optional) An array of parameter names and values.
*
* @dataProvider providerTestApplies
* @covers ::applies
*/
public function testApplies(bool $expected, ?string $route_name = NULL, array $parameter_map = []): void {
// Make some test doubles.
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$config_factory = $this->getConfigFactoryStub([]);
$forum_manager = $this->createMock('Drupal\forum\ForumManagerInterface');
$translation_manager = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface');
$map = [];
if ($parameter_map) {
foreach ($parameter_map as $parameter) {
$map[] = [
$parameter[0],
$parameter[1] === TRUE ? $this->getMockBuilder(Term::class)->disableOriginalConstructor()->getMock() : $parameter[1],
];
}
}
// Make an object to test.
$builder = new ForumListingBreadcrumbBuilder($entity_type_manager, $config_factory, $forum_manager, $translation_manager);
$route_match = $this->createMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->once())
->method('getRouteName')
->willReturn($route_name);
$route_match->expects($this->any())
->method('getParameter')
->willReturnMap($map);
$this->assertEquals($expected, $builder->applies($route_match));
}
/**
* Provides test data for testApplies().
*
* @return \Generator
* Datasets for testApplies(). Structured as such:
* - ForumListBreadcrumbBuilder::applies() expected result.
* - ForumListBreadcrumbBuilder::applies() $attributes input array.
*/
public static function providerTestApplies(): \Generator {
yield [FALSE];
yield [FALSE, 'NOT.forum.page'];
yield [FALSE, 'forum.page'];
yield [TRUE, 'forum.page', [['taxonomy_term', 'anything']]];
yield [TRUE, 'forum.page', [['taxonomy_term', TRUE]]];
}
/**
* Tests ForumListingBreadcrumbBuilder::build().
*
* @see \Drupal\forum\ForumListingBreadcrumbBuilder::build()
*
* @covers ::build
*/
public function testBuild(): void {
// Build all our dependencies, backwards.
$translation_manager = $this->getMockBuilder('Drupal\Core\StringTranslation\TranslationInterface')
->disableOriginalConstructor()
->getMock();
$prophecy = $this->prophesize('Drupal\taxonomy\Entity\Term');
$prophecy->label()->willReturn('Something');
$prophecy->id()->willReturn(1);
$prophecy->getCacheTags()->willReturn(['taxonomy_term:1']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$term1 = $prophecy->reveal();
$prophecy = $this->prophesize('Drupal\taxonomy\Entity\Term');
$prophecy->label()->willReturn('Something else');
$prophecy->id()->willReturn(2);
$prophecy->getCacheTags()->willReturn(['taxonomy_term:2']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$term2 = $prophecy->reveal();
$term_storage = $this->getMockBuilder(TermStorageInterface::class)->getMock();
$term_storage->expects($this->exactly(2))
->method('loadAllParents')
->willReturnOnConsecutiveCalls(
[$term1],
[$term1, $term2],
);
// The root forum.
$prophecy = $this->prophesize('Drupal\taxonomy\VocabularyInterface');
$prophecy->label()->willReturn('Fora_is_the_plural_of_forum');
$prophecy->id()->willReturn(5);
$prophecy->getCacheTags()->willReturn(['taxonomy_vocabulary:5']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$vocab_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
$vocab_storage->expects($this->any())
->method('load')
->willReturnMap([
['forums', $prophecy->reveal()],
]);
$entity_type_manager = $this->getMockBuilder(EntityTypeManagerInterface::class)
->disableOriginalConstructor()
->getMock();
$entity_type_manager->expects($this->any())
->method('getStorage')
->willReturnMap([
['taxonomy_vocabulary', $vocab_storage],
['taxonomy_term', $term_storage],
]);
$config_factory = $this->getConfigFactoryStub(
[
'forum.settings' => [
'vocabulary' => 'forums',
],
]
);
$forum_manager = $this->createMock('Drupal\forum\ForumManagerInterface');
// Build a breadcrumb builder to test.
$breadcrumb_builder = new ForumListingBreadcrumbBuilder($entity_type_manager, $config_factory, $forum_manager, $translation_manager);
// Add a translation manager for t().
$translation_manager = $this->getStringTranslationStub();
$breadcrumb_builder->setStringTranslation($translation_manager);
// The forum listing we need a breadcrumb back from.
$prophecy = $this->prophesize('Drupal\taxonomy\Entity\Term');
$prophecy->label()->willReturn('You_should_not_see_this');
$prophecy->id()->willReturn(23);
$prophecy->getCacheTags()->willReturn(['taxonomy_term:23']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$forum_listing = $prophecy->reveal();
// Our data set.
$route_match = $this->createMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->exactly(2))
->method('getParameter')
->with('taxonomy_term')
->willReturn($forum_listing);
// First test.
$expected1 = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Fora_is_the_plural_of_forum', 'forum.index'),
Link::createFromRoute('Something', 'forum.page', ['taxonomy_term' => 1]),
];
$breadcrumb = $breadcrumb_builder->build($route_match);
$this->assertEquals($expected1, $breadcrumb->getLinks());
$this->assertEqualsCanonicalizing(['route'], $breadcrumb->getCacheContexts());
$this->assertEqualsCanonicalizing(['taxonomy_term:1', 'taxonomy_term:23', 'taxonomy_vocabulary:5'], $breadcrumb->getCacheTags());
$this->assertEqualsCanonicalizing(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
// Second test.
$expected2 = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Fora_is_the_plural_of_forum', 'forum.index'),
Link::createFromRoute('Something else', 'forum.page', ['taxonomy_term' => 2]),
Link::createFromRoute('Something', 'forum.page', ['taxonomy_term' => 1]),
];
$breadcrumb = $breadcrumb_builder->build($route_match);
$this->assertEquals($expected2, $breadcrumb->getLinks());
$this->assertEqualsCanonicalizing(['route'], $breadcrumb->getCacheContexts());
$this->assertEqualsCanonicalizing(['taxonomy_term:1', 'taxonomy_term:2', 'taxonomy_term:23', 'taxonomy_vocabulary:5'], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
}

View File

@@ -0,0 +1,222 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Unit\Breadcrumb;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link;
use Drupal\forum\Breadcrumb\ForumNodeBreadcrumbBuilder;
use Drupal\node\Entity\Node;
use Drupal\taxonomy\TermStorageInterface;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\Container;
/**
* @coversDefaultClass \Drupal\forum\Breadcrumb\ForumNodeBreadcrumbBuilder
* @group forum
* @group legacy
*/
class ForumNodeBreadcrumbBuilderTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
$cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
$container = new Container();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
}
/**
* Tests ForumNodeBreadcrumbBuilder::applies().
*
* @param bool $expected
* ForumNodeBreadcrumbBuilder::applies() expected result.
* @param string|null $route_name
* (optional) A route name.
* @param array $parameter_map
* (optional) An array of parameter names and values.
*
* @dataProvider providerTestApplies
* @covers ::applies
*/
public function testApplies(bool $expected, ?string $route_name = NULL, array $parameter_map = []): void {
// Make some test doubles.
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$config_factory = $this->getConfigFactoryStub([]);
$map = [];
if ($parameter_map) {
foreach ($parameter_map as $parameter) {
$map[] = [
$parameter[0],
$parameter[1] === TRUE ? $this->getMockBuilder(Node::class)->disableOriginalConstructor()->getMock() : $parameter[1],
];
}
}
$forum_manager = $this->createMock('Drupal\forum\ForumManagerInterface');
$forum_manager->expects($this->any())
->method('checkNodeType')
->willReturn(TRUE);
$translation_manager = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface');
// Make an object to test.
$builder = new ForumNodeBreadcrumbBuilder($entity_type_manager, $config_factory, $forum_manager, $translation_manager);
$route_match = $this->createMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->once())
->method('getRouteName')
->willReturn($route_name);
$route_match->expects($this->any())
->method('getParameter')
->willReturnMap($map);
$this->assertEquals($expected, $builder->applies($route_match));
}
/**
* Provides test data for testApplies().
*
* Note that this test is incomplete, because we can't mock NodeInterface.
*
* @return \Generator
* Datasets for testApplies(). Structured as such:
* - ForumNodeBreadcrumbBuilder::applies() expected result.
* - ForumNodeBreadcrumbBuilder::applies() $attributes input array.
*/
public static function providerTestApplies(): \Generator {
yield [FALSE];
yield [FALSE, 'NOT.entity.node.canonical'];
yield [FALSE, 'entity.node.canonical'];
yield [FALSE, 'entity.node.canonical', [['node', NULL]]];
yield [TRUE, 'entity.node.canonical', [['node', TRUE]]];
}
/**
* Tests ForumNodeBreadcrumbBuilder::build().
*
* @see \Drupal\forum\ForumNodeBreadcrumbBuilder::build()
* @covers ::build
*/
public function testBuild(): void {
// Build all our dependencies, backwards.
$translation_manager = $this->getMockBuilder('Drupal\Core\StringTranslation\TranslationInterface')
->disableOriginalConstructor()
->getMock();
$prophecy = $this->prophesize('Drupal\taxonomy\Entity\Term');
$prophecy->label()->willReturn('Something');
$prophecy->id()->willReturn(1);
$prophecy->getCacheTags()->willReturn(['taxonomy_term:1']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$term1 = $prophecy->reveal();
$prophecy = $this->prophesize('Drupal\taxonomy\Entity\Term');
$prophecy->label()->willReturn('Something else');
$prophecy->id()->willReturn(2);
$prophecy->getCacheTags()->willReturn(['taxonomy_term:2']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$term2 = $prophecy->reveal();
$forum_manager = $this->getMockBuilder('Drupal\forum\ForumManagerInterface')
->disableOriginalConstructor()
->getMock();
$term_storage = $this->getMockBuilder(TermStorageInterface::class)->getMock();
$term_storage->expects($this->exactly(2))
->method('loadAllParents')
->willReturnOnConsecutiveCalls(
[$term1],
[$term1, $term2],
);
$prophecy = $this->prophesize('Drupal\taxonomy\VocabularyInterface');
$prophecy->label()->willReturn('Forums');
$prophecy->id()->willReturn(5);
$prophecy->getCacheTags()->willReturn(['taxonomy_vocabulary:5']);
$prophecy->getCacheContexts()->willReturn([]);
$prophecy->getCacheMaxAge()->willReturn(Cache::PERMANENT);
$vocab_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
$vocab_storage->expects($this->any())
->method('load')
->willReturnMap([
['forums', $prophecy->reveal()],
]);
$entity_type_manager = $this->getMockBuilder(EntityTypeManagerInterface::class)
->disableOriginalConstructor()
->getMock();
$entity_type_manager->expects($this->any())
->method('getStorage')
->willReturnMap([
['taxonomy_vocabulary', $vocab_storage],
['taxonomy_term', $term_storage],
]);
$config_factory = $this->getConfigFactoryStub(
[
'forum.settings' => [
'vocabulary' => 'forums',
],
]
);
// Build a breadcrumb builder to test.
$breadcrumb_builder = new ForumNodeBreadcrumbBuilder($entity_type_manager,
$config_factory,
$forum_manager,
$translation_manager);
// Add a translation manager for t().
$translation_manager = $this->getStringTranslationStub();
$breadcrumb_builder->setStringTranslation($translation_manager);
// The forum node we need a breadcrumb back from.
$forum_node = $this->getMockBuilder('Drupal\node\Entity\Node')
->disableOriginalConstructor()
->getMock();
// Our data set.
$route_match = $this->createMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->exactly(2))
->method('getParameter')
->with('node')
->willReturn($forum_node);
// First test.
$expected1 = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Forums', 'forum.index'),
Link::createFromRoute('Something', 'forum.page', ['taxonomy_term' => 1]),
];
$breadcrumb = $breadcrumb_builder->build($route_match);
$this->assertEquals($expected1, $breadcrumb->getLinks());
$this->assertEqualsCanonicalizing(['route'], $breadcrumb->getCacheContexts());
$this->assertEqualsCanonicalizing(['taxonomy_term:1', 'taxonomy_vocabulary:5'], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
// Second test.
$expected2 = [
Link::createFromRoute('Home', '<front>'),
Link::createFromRoute('Forums', 'forum.index'),
Link::createFromRoute('Something else', 'forum.page', ['taxonomy_term' => 2]),
Link::createFromRoute('Something', 'forum.page', ['taxonomy_term' => 1]),
];
$breadcrumb = $breadcrumb_builder->build($route_match);
$this->assertEquals($expected2, $breadcrumb->getLinks());
$this->assertEqualsCanonicalizing(['route'], $breadcrumb->getCacheContexts());
$this->assertEqualsCanonicalizing(['taxonomy_term:1', 'taxonomy_term:2', 'taxonomy_vocabulary:5'], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
}

View File

@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Unit;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\forum\ForumManager
* @group forum
* @group legacy
*/
class ForumManagerTest extends UnitTestCase {
/**
* Tests ForumManager::getIndex().
*/
public function testGetIndex(): void {
$entity_field_manager = $this->createMock(EntityFieldManagerInterface::class);
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
$storage = $this->getMockBuilder('\Drupal\taxonomy\VocabularyStorage')
->disableOriginalConstructor()
->getMock();
$config_factory = $this->createMock('\Drupal\Core\Config\ConfigFactoryInterface');
$config = $this->getMockBuilder('\Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_factory->expects($this->once())
->method('get')
->willReturn($config);
$config->expects($this->once())
->method('get')
->willReturn('forums');
$entity_type_manager->expects($this->once())
->method('getStorage')
->willReturn($storage);
// This is sufficient for testing purposes.
$term = new \stdClass();
$storage->expects($this->once())
->method('create')
->willReturn($term);
$connection = $this->getMockBuilder('\Drupal\Core\Database\Connection')
->disableOriginalConstructor()
->getMock();
$translation_manager = $this->getMockBuilder('\Drupal\Core\StringTranslation\TranslationManager')
->disableOriginalConstructor()
->getMock();
$comment_manager = $this->getMockBuilder('\Drupal\comment\CommentManagerInterface')
->disableOriginalConstructor()
->getMock();
$manager = $this->getMockBuilder('\Drupal\forum\ForumManager')
->onlyMethods(['getChildren'])
->setConstructorArgs([
$config_factory,
$entity_type_manager,
$connection,
$translation_manager,
$comment_manager,
$entity_field_manager,
])
->getMock();
$manager->expects($this->once())
->method('getChildren')
->willReturn([]);
// Get the index once.
$index1 = $manager->getIndex();
// Get it again. This should not return the previously generated index. If
// it does not, then the test will fail as the mocked methods will be called
// more than once.
$index2 = $manager->getIndex();
$this->assertEquals($index1, $index2);
}
}

View File

@@ -0,0 +1,241 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\forum\Unit;
use Drupal\Core\Url;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\forum\ForumUninstallValidator
* @group forum
* @group legacy
*/
class ForumUninstallValidatorTest extends UnitTestCase {
/**
* @var \Drupal\forum\ForumUninstallValidator|\PHPUnit\Framework\MockObject\MockObject
*/
protected $forumUninstallValidator;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->forumUninstallValidator = $this->getMockBuilder('Drupal\forum\ForumUninstallValidator')
->disableOriginalConstructor()
->onlyMethods(['hasForumNodes', 'hasTermsForVocabulary', 'getForumVocabulary'])
->getMock();
$this->forumUninstallValidator->setStringTranslation($this->getStringTranslationStub());
}
/**
* @covers ::validate
*/
public function testValidateNotForum(): void {
$this->forumUninstallValidator->expects($this->never())
->method('hasForumNodes');
$this->forumUninstallValidator->expects($this->never())
->method('hasTermsForVocabulary');
$this->forumUninstallValidator->expects($this->never())
->method('getForumVocabulary');
$module = 'not_forum';
$expected = [];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
/**
* @covers ::validate
*/
public function testValidate(): void {
$this->forumUninstallValidator->expects($this->once())
->method('hasForumNodes')
->willReturn(FALSE);
$vocabulary = $this->createMock('Drupal\taxonomy\VocabularyInterface');
$this->forumUninstallValidator->expects($this->once())
->method('getForumVocabulary')
->willReturn($vocabulary);
$this->forumUninstallValidator->expects($this->once())
->method('hasTermsForVocabulary')
->willReturn(FALSE);
$module = 'forum';
$expected = [];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
/**
* @covers ::validate
*/
public function testValidateHasForumNodes(): void {
$this->forumUninstallValidator->expects($this->once())
->method('hasForumNodes')
->willReturn(TRUE);
$vocabulary = $this->createMock('Drupal\taxonomy\VocabularyInterface');
$this->forumUninstallValidator->expects($this->once())
->method('getForumVocabulary')
->willReturn($vocabulary);
$this->forumUninstallValidator->expects($this->once())
->method('hasTermsForVocabulary')
->willReturn(FALSE);
$module = 'forum';
$expected = [
'To uninstall Forum, first delete all <em>Forum</em> content',
];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
/**
* @covers ::validate
*/
public function testValidateHasTermsForVocabularyWithNodesAccess(): void {
$this->forumUninstallValidator->expects($this->once())
->method('hasForumNodes')
->willReturn(TRUE);
$url = $this->prophesize(Url::class);
$url->toString()->willReturn('/path/to/vocabulary/overview');
$vocabulary = $this->createMock('Drupal\taxonomy\VocabularyInterface');
$vocabulary->expects($this->once())
->method('label')
->willReturn('Vocabulary label');
$vocabulary->expects($this->once())
->method('toUrl')
->willReturn($url->reveal());
$vocabulary->expects($this->once())
->method('access')
->willReturn(TRUE);
$this->forumUninstallValidator->expects($this->once())
->method('getForumVocabulary')
->willReturn($vocabulary);
$this->forumUninstallValidator->expects($this->once())
->method('hasTermsForVocabulary')
->willReturn(TRUE);
$module = 'forum';
$expected = [
'To uninstall Forum, first delete all <em>Forum</em> content',
'To uninstall Forum, first delete all <a href="/path/to/vocabulary/overview"><em class="placeholder">Vocabulary label</em></a> terms',
];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
/**
* @covers ::validate
*/
public function testValidateHasTermsForVocabularyWithNodesNoAccess(): void {
$this->forumUninstallValidator->expects($this->once())
->method('hasForumNodes')
->willReturn(TRUE);
$vocabulary = $this->createMock('Drupal\taxonomy\VocabularyInterface');
$vocabulary->expects($this->once())
->method('label')
->willReturn('Vocabulary label');
$vocabulary->expects($this->never())
->method('toUrl');
$vocabulary->expects($this->once())
->method('access')
->willReturn(FALSE);
$this->forumUninstallValidator->expects($this->once())
->method('getForumVocabulary')
->willReturn($vocabulary);
$this->forumUninstallValidator->expects($this->once())
->method('hasTermsForVocabulary')
->willReturn(TRUE);
$module = 'forum';
$expected = [
'To uninstall Forum, first delete all <em>Forum</em> content',
'To uninstall Forum, first delete all <em class="placeholder">Vocabulary label</em> terms',
];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
/**
* @covers ::validate
*/
public function testValidateHasTermsForVocabularyAccess(): void {
$this->forumUninstallValidator->expects($this->once())
->method('hasForumNodes')
->willReturn(FALSE);
$url = $this->prophesize(Url::class);
$url->toString()->willReturn('/path/to/vocabulary/overview');
$vocabulary = $this->createMock('Drupal\taxonomy\VocabularyInterface');
$vocabulary->expects($this->once())
->method('toUrl')
->willReturn($url->reveal());
$vocabulary->expects($this->once())
->method('label')
->willReturn('Vocabulary label');
$vocabulary->expects($this->once())
->method('access')
->willReturn(TRUE);
$this->forumUninstallValidator->expects($this->once())
->method('getForumVocabulary')
->willReturn($vocabulary);
$this->forumUninstallValidator->expects($this->once())
->method('hasTermsForVocabulary')
->willReturn(TRUE);
$module = 'forum';
$expected = [
'To uninstall Forum, first delete all <a href="/path/to/vocabulary/overview"><em class="placeholder">Vocabulary label</em></a> terms',
];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
/**
* @covers ::validate
*/
public function testValidateHasTermsForVocabularyNoAccess(): void {
$this->forumUninstallValidator->expects($this->once())
->method('hasForumNodes')
->willReturn(FALSE);
$vocabulary = $this->createMock('Drupal\taxonomy\VocabularyInterface');
$vocabulary->expects($this->once())
->method('label')
->willReturn('Vocabulary label');
$vocabulary->expects($this->never())
->method('toUrl');
$vocabulary->expects($this->once())
->method('access')
->willReturn(FALSE);
$this->forumUninstallValidator->expects($this->once())
->method('getForumVocabulary')
->willReturn($vocabulary);
$this->forumUninstallValidator->expects($this->once())
->method('hasTermsForVocabulary')
->willReturn(TRUE);
$module = 'forum';
$expected = [
'To uninstall Forum, first delete all <em class="placeholder">Vocabulary label</em> terms',
];
$reasons = $this->forumUninstallValidator->validate($module);
$this->assertEquals($expected, $reasons);
}
}