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

View File

@@ -0,0 +1,24 @@
<?php
/**
* @file
* Empties the description of the `tags` vocabulary.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
$data = $connection->select('config')
->condition('name', 'taxonomy.vocabulary.tags')
->fields('config', ['data'])
->execute()
->fetchField();
$data = unserialize($data);
$data['description'] = "\n";
$connection->update('config')
->condition('name', 'taxonomy.vocabulary.tags')
->fields([
'data' => serialize($data),
])
->execute();

View File

@@ -0,0 +1,12 @@
name: 'Taxonomy CRUD tests'
type: module
description: 'Provides 3rd party settings for vocabulary.'
package: Testing
# version: VERSION
dependencies:
- drupal:taxonomy
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,15 @@
<?php
/**
* @file
* Provides hook implementations for testing purposes.
*/
use Drupal\taxonomy\VocabularyInterface;
/**
* Implements hook_ENTITY_TYPE_presave() for taxonomy_vocabulary entities.
*/
function taxonomy_crud_taxonomy_vocabulary_presave(VocabularyInterface $vocabulary) {
$vocabulary->setThirdPartySetting('taxonomy_crud', 'foo', 'bar');
}

View File

@@ -0,0 +1,10 @@
name: 'Taxonomy term configurable display module tests'
type: module
description: 'Support module for taxonomy_term \Drupal\Core\Field\BaseFieldDefinition::setDisplayConfigurable() testing.'
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,26 @@
<?php
/**
* @file
* Tests configurable displays for taxonomy_term base fields.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_entity_base_field_info_alter().
*/
function taxonomy_term_display_configurable_test_entity_base_field_info_alter(&$base_field_definitions, EntityTypeInterface $entity_type) {
if ($entity_type->id() === 'taxonomy_term') {
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $base_field_definitions */
$base_field_definitions['name']->setDisplayConfigurable('view', TRUE);
}
}
/**
* Implements hook_entity_type_build().
*/
function taxonomy_term_display_configurable_test_entity_type_build(array &$entity_types) {
// Allow skipping of extra preprocessing for configurable display.
$entity_types['taxonomy_term']->set('enable_base_field_custom_preprocess_skipping', TRUE);
}

View File

@@ -0,0 +1,32 @@
id: taxonomy_term_stub_test
label: Taxonomy term stub
migration_tags:
- Import and rollback test
source:
plugin: embedded_data
data_rows:
-
id: 1
vocab: 1
name: music
parent: 2
ids:
id:
type: integer
process:
tid: id
vid: vocab
name: name
weight: weight
parent:
plugin: migration_lookup
migration: taxonomy_term_stub_test
source: parent
destination:
plugin: entity:taxonomy_term
migration_dependencies:
required:
- vocabularies
provider:
- migrate_drupal
- taxonomy

View File

@@ -0,0 +1,13 @@
name: 'Taxonomy Migrate stub test'
type: module
description: 'Provides a migration plugin for stub testing.'
package: Testing
# version: VERSION
dependencies:
- drupal:taxonomy
- drupal:migrate
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Drupal\taxonomy_test\Plugin\views\argument;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\taxonomy\Plugin\views\argument\Taxonomy;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Test argument handler for testing deprecation in Taxonomy argument plugin.
*
* Intentionally setup our properties and constructor as Drupal 10.2.x and
* earlier used in the Taxonomy argument handler.
*
* @ingroup views_argument_handlers
*
* @ViewsArgument("taxonomy_views_argument_test")
*/
class TaxonomyViewsArgumentTest extends Taxonomy {
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
protected EntityStorageInterface $termStorage,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $termStorage);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')->getStorage('taxonomy_term')
);
}
}

View File

@@ -0,0 +1,12 @@
name: 'Taxonomy test'
type: module
description: 'Provides test hook implementations for taxonomy tests'
package: Testing
# version: VERSION
dependencies:
- drupal:taxonomy
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,60 @@
<?php
/**
* @file
* Provides test hook implementations for taxonomy tests.
*/
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_query_alter().
*/
function taxonomy_test_query_alter(AlterableInterface $query) {
$value = \Drupal::state()->get(__FUNCTION__);
if (isset($value)) {
\Drupal::state()->set(__FUNCTION__, ++$value);
}
}
/**
* Implements hook_query_TAG_alter().
*/
function taxonomy_test_query_term_access_alter(AlterableInterface $query) {
$value = \Drupal::state()->get(__FUNCTION__);
if (isset($value)) {
\Drupal::state()->set(__FUNCTION__, ++$value);
}
}
/**
* Implements hook_query_TAG_alter().
*/
function taxonomy_test_query_taxonomy_term_access_alter(AlterableInterface $query) {
$value = \Drupal::state()->get(__FUNCTION__);
if (isset($value)) {
\Drupal::state()->set(__FUNCTION__, ++$value);
}
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for the taxonomy term form.
*/
function taxonomy_test_form_taxonomy_term_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if (\Drupal::state()->get('taxonomy_test.disable_parent_form_element', FALSE)) {
$form['relations']['parent']['#disabled'] = TRUE;
}
}
/**
* Implements hook_ENTITY_TYPE_load() for the taxonomy term.
*/
function taxonomy_test_taxonomy_term_load($entities) {
$value = \Drupal::state()->get(__FUNCTION__);
// Only record loaded terms is the test has set this to an empty array.
if (is_array($value)) {
$value = array_merge($value, array_keys($entities));
\Drupal::state()->set(__FUNCTION__, array_unique($value));
}
}

View File

@@ -0,0 +1,13 @@
name: 'Taxonomy test views'
type: module
description: 'Provides default views for views taxonomy tests.'
package: Testing
# version: VERSION
dependencies:
- drupal:taxonomy
- 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,257 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: taxonomy_all_terms_test
label: taxonomy_all_terms_test
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
term_node_tid:
id: term_node_tid
table: node_field_data
field: term_node_tid
relationship: none
group_type: group
admin_label: ''
label: ''
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: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
type: separator
separator: ', '
link_to_taxonomy: true
limit: false
vids:
tags: '0'
entity_type: node
plugin_id: taxonomy_index_tid
filters: { }
sorts:
nid:
id: nid
table: node
field: nid
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
entity_type: node
entity_field: nid
plugin_id: standard
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
max-age: 0
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: taxonomy_all_terms_test
cache_metadata:
contexts:
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
cacheable: false
page_2:
display_plugin: page
id: page_2
display_title: 'Token tests'
position: 2
display_options:
display_extenders: { }
display_description: ''
fields:
term_node_tid:
id: term_node_tid
table: node_field_data
field: term_node_tid
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: true
text: "Term: {{ term_node_tid }}<br />\nThe taxonomy term ID for the term: {{ term_node_tid__tid }}<br />\nThe taxonomy term name for the term: {{ term_node_tid__name }}<br />\nThe machine name for the vocabulary the term belongs to: {{ term_node_tid__vocabulary_vid }}<br />\nThe name for the vocabulary the term belongs to: {{ term_node_tid__vocabulary }}<br />"
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: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
type: separator
separator: '<br />'
link_to_taxonomy: false
limit: false
vids:
tags: '0'
entity_type: node
plugin_id: taxonomy_index_tid
defaults:
fields: false
path: taxonomy_all_terms_token_test
cache_metadata:
contexts:
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
max-age: 0

View File

@@ -0,0 +1,170 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: taxonomy_default_argument_test
label: taxonomy_default_argument_test
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 20, 40, 60'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: 'entity:taxonomy_term'
options:
relationship: none
view_mode: default
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
plugin_id: field
type: string
settings:
link_to_entity: true
entity_type: taxonomy_term
entity_field: name
filters: { }
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
tid:
id: tid
table: taxonomy_term_field_data
field: tid
relationship: none
group_type: group
admin_label: ''
default_action: default
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: taxonomy_tid
default_argument_options:
term_page: '1'
node: true
anyall: ','
limit: false
vids: { }
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
plugin_id: taxonomy
entity_type: taxonomy_term
entity_field: tid
rendering_language: '***LANGUAGE_entity_translation***'
page_1:
display_plugin: page
id: page_1
display_title: Page
position: null
display_options:
path: taxonomy_default_argument_test

View File

@@ -0,0 +1,337 @@
langcode: en
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
module:
- node
- taxonomy
- user
id: taxonomy_translated_term_name_test
label: 'Taxonomy Translated Term Name Test'
module: taxonomy
description: 'Content belonging to a certain taxonomy term.'
tag: default
base_table: node_field_data
base_field: nid
display:
default:
id: default
display_title: Default
display_plugin: default
position: 0
display_options:
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: 0
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
sorts:
sticky:
id: sticky
table: taxonomy_index
field: sticky
order: DESC
plugin_id: standard
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
created:
id: created
table: taxonomy_index
field: created
order: DESC
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
arguments:
term_node_tid_depth:
id: term_node_tid_depth
table: node_field_data
field: term_node_tid_depth
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: true
title: '{{ arguments.term_node_tid_depth }}'
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: 'entity:taxonomy_term'
fail: 'not found'
validate_options:
access: true
operation: view
multiple: 0
bundles: { }
depth: 2
break_phrase: false
use_taxonomy_term_path: false
entity_type: node
plugin_id: taxonomy_index_tid_depth
filters:
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
operator: in
value:
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: language
entity_type: node
entity_field: langcode
status:
id: status
table: taxonomy_index
field: status
relationship: none
group_type: group
admin_label: ''
operator: '='
value: '1'
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
operator_limit_selection: false
operator_list: { }
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: boolean
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: 'entity:node'
options:
view_mode: teaser
header:
entity_taxonomy_term:
id: entity_taxonomy_term
table: views
field: entity_taxonomy_term
relationship: none
group_type: group
admin_label: ''
empty: true
tokenize: true
target: '{{ raw_arguments.tid }}'
view_mode: full
bypass_access: false
plugin_id: entity
footer: { }
empty: { }
relationships: { }
fields: { }
display_extenders: { }
link_url: ''
link_display: page_1
cache_metadata:
contexts:
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
max-age: -1
tags: { }
page_1:
id: page_1
display_title: Page
display_plugin: page
position: 1
display_options:
query:
type: views_query
options: { }
display_extenders: { }
path: taxonomy/test/%
cache_metadata:
max-age: -1
contexts: { }
tags: { }
page_2:
id: page_2
display_title: Page
display_plugin: page
position: 1
display_options:
arguments:
tid:
id: tid
table: taxonomy_term_field_data
field: tid
relationship: term_node_tid
group_type: group
admin_label: ''
entity_type: taxonomy_term
entity_field: tid
plugin_id: taxonomy
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
count: true
override: false
items_per_page: 25
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
query:
type: views_query
options: { }
defaults:
relationships: false
arguments: false
relationships:
term_node_tid:
id: term_node_tid
table: node_field_data
field: term_node_tid
relationship: none
group_type: group
admin_label: term
entity_type: node
plugin_id: node_term_data
required: false
vids: { }
display_extenders: { }
path: taxonomy/test2/%
cache_metadata:
max-age: -1
contexts: { }
tags: { }

View File

@@ -0,0 +1,225 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_argument_taxonomy_index_tid_depth
label: test_argument_taxonomy_index_tid_depth
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
settings:
link_to_entity: true
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
entity_type: node
entity_field: created
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
term_node_tid_depth:
id: term_node_tid_depth
table: node_field_data
field: term_node_tid_depth
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: true
title: '{{ arguments.term_node_tid_depth }}'
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
depth: 0
break_phrase: false
use_taxonomy_term_path: false
entity_type: node
plugin_id: taxonomy_index_tid_depth
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
cacheable: false
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test_argument_taxonomy_index_tid_depth
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
cacheable: false

View File

@@ -0,0 +1,171 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: test_argument_taxonomy_vocabulary
label: test_argument_taxonomy_vocabulary
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
offset: 0
style:
type: default
row:
type: 'entity:taxonomy_term'
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
entity_type: taxonomy_term
entity_field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
type: string
settings:
link_to_entity: true
plugin_id: term_name
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
click_sort_column: value
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
convert_spaces: false
filters: { }
sorts: { }
title: ''
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
vid:
id: vid
table: taxonomy_term_field_data
field: vid
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
entity_type: taxonomy_term
entity_field: vid
plugin_id: vocabulary_vid
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- user.permissions
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test_argument_taxonomy_vocabulary
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- user.permissions
tags: { }

View File

@@ -0,0 +1,423 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: test_field_filters
label: 'Test field filters'
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: 0
offset: 0
style:
type: default
row:
type: 'entity:taxonomy_term'
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
plugin_id: field
type: string
settings:
link_to_entity: true
entity_type: taxonomy_term
entity_field: name
filters:
name:
id: name
table: taxonomy_term_field_data
field: name
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Comida
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
entity_type: taxonomy_term
entity_field: name
sorts: { }
title: 'Name filter page'
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
page_dc:
display_plugin: page
id: page_dc
display_title: 'Description Comida'
position: 3
display_options:
display_description: ''
path: test-desc-filter
filters:
description__value:
id: description__value
table: taxonomy_term_field_data
field: description__value
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Comida
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
entity_type: taxonomy_term
entity_field: description
defaults:
filters: false
filter_groups: false
title: false
filter_groups:
operator: AND
groups:
1: AND
title: 'Description filter page'
page_dp:
display_plugin: page
id: page_dp
display_title: 'Description Comida'
position: 3
display_options:
display_description: ''
path: test-desc-paris
filters:
description__value:
id: description__value
table: taxonomy_term_field_data
field: description__value
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Paris
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
entity_type: taxonomy_term
entity_field: description
defaults:
filters: false
filter_groups: false
title: false
filter_groups:
operator: AND
groups:
1: AND
title: 'Description filter page'
page_fc:
display_plugin: page
id: page_fc
display_title: 'Field Comida'
position: 3
display_options:
display_description: ''
path: test-field-filter
filters:
field_foo_value:
id: field_foo_value
table: taxonomy_term__field_foo
field: field_foo_value
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Comida
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
entity_type: taxonomy_term
entity_field: field_foo
defaults:
filters: false
filter_groups: false
title: false
filter_groups:
operator: AND
groups:
1: AND
title: 'Field filter page'
page_fp:
display_plugin: page
id: page_fp
display_title: 'Field Paris'
position: 3
display_options:
display_description: ''
path: test-field-paris
filters:
field_foo_value:
id: field_foo_value
table: taxonomy_term__field_foo
field: field_foo_value
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Paris
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
entity_type: taxonomy_term
entity_field: field_foo
defaults:
filters: false
filter_groups: false
title: false
filter_groups:
operator: AND
groups:
1: AND
title: 'Field filter page'
page_nc:
display_plugin: page
id: page_nc
display_title: 'Name Comida'
position: 1
display_options:
path: test-name-filter
display_description: ''
page_np:
display_plugin: page
id: page_np
display_title: 'Name Paris'
position: 1
display_options:
path: test-name-paris
display_description: ''
filters:
name:
id: name
table: taxonomy_term_field_data
field: name
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Paris
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
entity_type: taxonomy_term
entity_field: name
defaults:
filters: false
filter_groups: false
filter_groups:
operator: AND
groups:
1: AND

View File

@@ -0,0 +1,185 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_filter_taxonomy_index_tid
label: test_filter_taxonomy_index_tid
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
plugin_id: field
entity_type: node
entity_field: title
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: '0'
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
tid:
id: tid
table: taxonomy_index
field: tid
relationship: none
group_type: group
admin_label: ''
operator: or
value:
- 2
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
type: select
limit: true
vid: tags
hierarchy: true
error_message: true
plugin_id: taxonomy_index_tid
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 2
display_options:
display_extenders: { }
path: test-filter-taxonomy-index-tid

View File

@@ -0,0 +1,186 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_filter_taxonomy_index_tid__non_existing_dependency
label: test_filter_taxonomy_index_tid__non_existing_dependency
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
plugin_id: field
entity_type: node
entity_field: title
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: '0'
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
tid:
id: tid
table: taxonomy_index
field: tid
relationship: none
group_type: group
admin_label: ''
operator: or
value:
- 3
- 4
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
type: select
limit: true
vid: tags
hierarchy: true
error_message: true
plugin_id: taxonomy_index_tid
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 2
display_options:
display_extenders: { }
path: test-filter-taxonomy-index-tid

View File

@@ -0,0 +1,192 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_filter_taxonomy_index_tid_depth
label: test_filter_taxonomy_index_tid_depth
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
plugin_id: field
entity_type: node
entity_field: title
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: '0'
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
tid_depth:
id: tid_depth
table: node_field_data
field: term_node_tid_depth
relationship: none
group_type: group
admin_label: ''
operator: or
value: { }
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
type: select
limit: true
vid: views_testing_tags
hierarchy: true
depth: -2
error_message: true
plugin_id: taxonomy_index_tid_depth
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
entity_type: node
entity_field: created
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }

View File

@@ -0,0 +1,82 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_groupwise_term
label: test_groupwise
module: views
description: ''
tag: default
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_options:
access:
type: perm
cache:
type: tag
exposed_form:
type: basic
fields:
name:
field: name
id: name
table: taxonomy_term_field_data
plugin_id: term_name
type: string
settings:
link_to_entity: true
convert_spaces: false
entity_type: taxonomy_term
entity_field: name
nid:
field: nid
id: nid
relationship: tid_representative
table: node_field_data
plugin_id: node
entity_type: node
entity_field: nid
pager:
options:
items_per_page: 10
type: full
query:
type: views_query
relationships:
tid_representative:
admin_label: 'Representative node'
field: tid_representative
group_type: group
id: tid_representative
relationship: none
required: false
subquery_namespace: ''
subquery_order: DESC
subquery_regenerate: true
subquery_sort: node_field_data.nid
subquery_view: ''
table: taxonomy_term_field_data
plugin_id: groupwise_max
row:
type: fields
sorts:
tid:
field: tid
id: tid
order: DESC
table: taxonomy_term_field_data
plugin_id: standard
entity_type: taxonomy_term
entity_field: tid
style:
type: default
title: test_groupwise
display_plugin: default
display_title: Default
id: default
position: 0

View File

@@ -0,0 +1,271 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
- taxonomy.vocabulary.tags
module:
- node
- taxonomy
- user
id: test_taxonomy_exposed_grouped_filter
label: 'Test Taxonomy Exposed Grouped Filter'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
settings:
link_to_entity: true
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
type:
id: type
table: node_field_data
field: type
value:
article: article
entity_type: node
entity_field: type
plugin_id: bundle
expose:
operator_limit_selection: false
operator_list: { }
field_views_testing_tags_target_id:
id: field_views_testing_tags_target_id
table: node__field_views_testing_tags
field: field_views_testing_tags_target_id
relationship: none
group_type: group
admin_label: ''
operator: or
value: { }
group: 1
exposed: true
expose:
operator_id: field_views_testing_tags_target_id_op
label: 'views_testing_tags (field_views_testing_tags)'
description: null
use_operator: false
operator: field_views_testing_tags_target_id_op
operator_limit_selection: false
operator_list: { }
identifier: field_views_testing_tags_target_id
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: true
group_info:
label: 'views_testing_tags (field_views_testing_tags)'
description: ''
identifier: field_views_testing_tags_target_id
optional: true
widget: radios
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items:
1:
title: 'Group 1'
operator: or
value:
1: '1'
2: '2'
2:
title: 'Group 2'
operator: or
value:
3: '3'
4: '4'
3:
title: 'Group 3'
operator: or
value:
1: '1'
5: '5'
reduce_duplicates: false
type: select
limit: true
vid: tags
hierarchy: false
error_message: true
plugin_id: taxonomy_index_tid
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
entity_type: node
entity_field: created
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
field_identifier: ''
granularity: second
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user
- 'user.node_grants:view'
- user.permissions
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test-taxonomy-exposed-grouped-filter
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user
- 'user.node_grants:view'
- user.permissions
tags: { }

View File

@@ -0,0 +1,234 @@
langcode: en
status: true
dependencies:
config:
- taxonomy.vocabulary.tags
module:
- taxonomy
- user
id: test_taxonomy_name_argument
label: 'Test taxonomy term argument'
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
entity_type: taxonomy_term
entity_field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
type: string
settings:
link_to_entity: true
plugin_id: term_name
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
click_sort_column: value
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
convert_spaces: false
filters:
vid:
id: vid
table: taxonomy_term_field_data
field: vid
relationship: none
group_type: group
admin_label: ''
operator: in
value:
tags: tags
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
operator_limit_selection: false
operator_list: { }
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: taxonomy_term
entity_field: vid
plugin_id: bundle
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
name:
id: name
table: taxonomy_term_field_data
field: name
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: taxonomy_term_name
fail: 'not found'
validate_options:
bundles:
views_testing_tags: views_testing_tags
operation: view
access: false
transform: false
glossary: false
limit: 0
case: none
path_case: none
transform_dash: false
break_phrase: false
add_table: false
require_value: false
entity_type: taxonomy_term
entity_field: name
plugin_id: string
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }

View File

@@ -0,0 +1,88 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_taxonomy_node_term_data
label: test_taxonomy_node_term_data
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_options:
access:
type: perm
arguments:
tid:
default_argument_type: fixed
field: tid
id: tid
relationship: term_node_tid
summary:
format: default_summary
number_of_records: 0
summary_options:
items_per_page: 25
table: taxonomy_term_field_data
plugin_id: taxonomy
entity_type: taxonomy_term
entity_field: tid
tid_1:
default_argument_type: fixed
field: tid
id: tid_1
relationship: term_node_tid_1
summary:
format: default_summary
number_of_records: 0
summary_options:
items_per_page: 25
table: taxonomy_term_field_data
plugin_id: taxonomy
entity_type: taxonomy_term
entity_field: tid
cache:
type: tag
exposed_form:
type: basic
pager:
type: full
query:
type: views_query
relationships:
term_node_tid:
field: term_node_tid
id: term_node_tid
admin_label: 'Term #1'
table: node_field_data
vids: {}
plugin_id: node_term_data
term_node_tid_1:
field: term_node_tid
id: term_node_tid_1
admin_label: 'Term #2'
table: node_field_data
vids: {}
plugin_id: node_term_data
sorts:
nid:
field: nid
id: nid
order: DESC
table: node_field_data
plugin_id: standard
entity_type: node
entity_field: nid
style:
type: default
row:
type: 'entity:node'
display_plugin: default
display_title: Default
id: default
position: 0

View File

@@ -0,0 +1,133 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: test_taxonomy_parent
label: test_taxonomy_parent
module: views
description: ''
tag: ''
base_table: taxonomy_term_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
name:
id: name
table: taxonomy_term_data
field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: 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_alter_empty: true
plugin_id: field
type: string
settings:
link_to_entity: true
entity_type: taxonomy_term
entity_field: name
filters: { }
sorts: { }
header: { }
footer: { }
empty: { }
relationships:
parent:
id: parent
table: taxonomy_term__parent
field: parent_target_id
relationship: none
group_type: group
admin_label: Parent
required: true
plugin_id: standard
arguments: { }

View File

@@ -0,0 +1,157 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: test_taxonomy_term_name
label: test_taxonomy_term_name
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 20, 40, 60'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
relationship: none
group_type: group
admin_label: ''
label: Name
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
plugin_id: term_name
type: string
settings:
link_to_entity: false
convert_spaces: false
entity_type: taxonomy_term
entity_field: name
filters: { }
sorts:
tid:
id: tid
table: taxonomy_term_field_data
field: tid
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
plugin_id: standard
entity_type: taxonomy_term
entity_field: tid
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }

View File

@@ -0,0 +1,201 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
- user
id: test_taxonomy_term_relationship
label: test_taxonomy_term_relationship
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
tid:
id: tid
table: taxonomy_term_field_data
field: tid
entity_type: taxonomy_term
entity_field: tid
plugin_id: field
tid_1:
id: tid_1
table: taxonomy_term_field_data
field: tid
relationship: parent
group_type: group
admin_label: ''
label: ''
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: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: taxonomy_term
entity_field: tid
plugin_id: field
nid:
id: nid
table: node_field_data
field: nid
entity_type: node
entity_field: nid
plugin_id: field
relationship: nid
filters: { }
sorts:
tid:
id: tid
table: taxonomy_term_field_data
field: tid
entity_type: taxonomy_term
entity_field: tid
plugin_id: standard
header: { }
footer: { }
empty: { }
relationships:
nid:
id: nid
table: taxonomy_index
field: nid
relationship: none
group_type: group
admin_label: node
required: false
plugin_id: standard
parent:
id: parent
table: taxonomy_term__parent
field: parent_target_id
relationship: none
group_type: group
admin_label: Parent
required: false
plugin_id: standard
arguments: { }
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
max-age: 0

View File

@@ -0,0 +1,156 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: test_taxonomy_tid_field
label: test_taxonomy_tid_field
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 20, 40, 60'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
relationship: none
group_type: group
admin_label: ''
label: Name
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
plugin_id: field
type: string
settings:
link_to_entity: true
entity_type: taxonomy_term
entity_field: name
filters: { }
sorts:
tid:
id: tid
table: taxonomy_term_field_data
field: tid
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
plugin_id: standard
entity_type: taxonomy_term
entity_field: tid
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }

View File

@@ -0,0 +1,177 @@
uuid: 67bc4e15-f415-4747-8908-d3d56088fbbe
langcode: en
status: true
dependencies:
module:
- taxonomy
- user
id: test_taxonomy_vid_field
label: test_taxonomy_vid_field
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
vid:
id: vid
table: taxonomy_term_field_data
field: vid
relationship: none
group_type: group
admin_label: 'Vocabulary name'
label: ''
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: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: false
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: taxonomy_term
entity_field: vid
plugin_id: field
filters: { }
sorts:
vid:
id: vid
table: taxonomy_term_field_data
field: vid
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
entity_type: taxonomy_term
entity_field: vid
plugin_id: standard
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- user.permissions
tags: { }

View File

@@ -0,0 +1,80 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
id: test_term_show_entity
label: test_term_show_entity
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 0
display_options:
access:
type: none
options: { }
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
offset: 0
style:
type: default
row:
type: 'entity:taxonomy_term'
fields: { }
filters: { }
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test_term_show_entity
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
tags: { }

View File

@@ -0,0 +1,19 @@
<?php
namespace Drupal\vocabulary_serialization_test;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\taxonomy\VocabularyInterface;
class VocabularyResponse extends CacheableResponse {
/**
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
public function setVocabulary(VocabularyInterface $vocabulary) {
$this->vocabulary = $vocabulary;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Drupal\vocabulary_serialization_test;
use Drupal\taxonomy\VocabularyInterface;
class VocabularySerializationTestController {
public function vocabularyResponse(VocabularyInterface $taxonomy_vocabulary) {
$response = new VocabularyResponse('this is the output');
$response->setVocabulary($taxonomy_vocabulary);
return $response;
}
}

View File

@@ -0,0 +1,11 @@
name: 'Vocabulary serialization test'
type: module
package: Testing
# version: VERSION
dependencies:
- drupal:taxonomy
# 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 @@
vocabulary_serialization_test:
path: '/vocabulary_serialization_test/{taxonomy_vocabulary}'
defaults:
_controller: '\Drupal\vocabulary_serialization_test\VocabularySerializationTestController::vocabularyResponse'
requirements:
_access: 'TRUE'

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Posts an article with a taxonomy term and a date prior to 1970.
*
* @group taxonomy
*/
class EarlyDateTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['node', 'datetime'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create a tags vocabulary for the 'article' content type.
$vocabulary = Vocabulary::create([
'name' => 'Tags',
'vid' => 'tags',
]);
$vocabulary->save();
$field_name = 'field_' . $vocabulary->id();
$handler_settings = [
'target_bundles' => [
$vocabulary->id() => $vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $field_name, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
\Drupal::service('entity_display.repository')
->getFormDisplay('node', 'article')
->setComponent($field_name, [
'type' => 'entity_reference_autocomplete_tags',
])
->save();
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'administer nodes',
'bypass node access',
]));
}
/**
* Tests taxonomy functionality with nodes prior to 1970.
*/
public function testTaxonomyEarlyDateNode(): void {
// Posts an article with a taxonomy term and a date prior to 1970.
$date = new DrupalDateTime('1969-01-01 00:00:00');
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['created[0][value][date]'] = $date->format('Y-m-d');
$edit['created[0][value][time]'] = $date->format('H:i:s');
$edit['body[0][value]'] = $this->randomMachineName();
$edit['field_tags[target_id]'] = $this->randomMachineName();
$this->drupalGet('node/add/article');
$this->submitForm($edit, 'Save');
// Checks that the node has been saved.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertEquals($date->getTimestamp(), $node->getCreatedTime(), 'Legacy node was saved with the right date.');
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
/**
* Generic module test for taxonomy.
*
* @group taxonomy
*/
class GenericTest extends GenericModuleTestBase {}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
* @group #slow
*/
class TermJsonAnonTest extends TermResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
* @group #slow
*/
class TermJsonBasicAuthTest extends TermResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
* @group #slow
*/
class TermJsonCookieTest extends TermResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,387 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Core\Cache\Cache;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use GuzzleHttp\RequestOptions;
abstract class TermResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'path'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'taxonomy_term';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed' => NULL,
];
/**
* @var \Drupal\taxonomy\TermInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access content']);
break;
case 'POST':
$this->grantPermissionsToTestedRole(['create terms in camelids']);
break;
case 'PATCH':
// Grant the 'create url aliases' permission to test the case when
// the path field is accessible, see
// \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase
// for a negative test.
$this->grantPermissionsToTestedRole(['edit terms in camelids', 'create url aliases']);
break;
case 'DELETE':
$this->grantPermissionsToTestedRole(['delete terms in camelids']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$vocabulary = Vocabulary::load('camelids');
if (!$vocabulary) {
// Create a "Camelids" vocabulary.
$vocabulary = Vocabulary::create([
'name' => 'Camelids',
'vid' => 'camelids',
]);
$vocabulary->save();
}
// Create a "Llama" taxonomy term.
$term = Term::create(['vid' => $vocabulary->id()])
->setName('Llama')
->setDescription("It is a little known fact that llamas cannot count higher than seven.")
->setChangedTime(123456789)
->set('path', '/llama');
$term->save();
return $term;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
// We test with multiple parent terms, and combinations thereof.
// @see ::createEntity()
// @see ::testGet()
// @see ::testGetTermWithParent()
// @see ::providerTestGetTermWithParent()
$parent_term_ids = [];
for ($i = 0; $i < $this->entity->get('parent')->count(); $i++) {
$parent_term_ids[$i] = (int) $this->entity->get('parent')[$i]->target_id;
}
$expected_parent_normalization = FALSE;
switch ($parent_term_ids) {
case [0]:
$expected_parent_normalization = [
[
'target_id' => NULL,
],
];
break;
case [2]:
$expected_parent_normalization = [
[
'target_id' => 2,
'target_type' => 'taxonomy_term',
'target_uuid' => Term::load(2)->uuid(),
'url' => base_path() . 'taxonomy/term/2',
],
];
break;
case [0, 2]:
$expected_parent_normalization = [
[
'target_id' => NULL,
],
[
'target_id' => 2,
'target_type' => 'taxonomy_term',
'target_uuid' => Term::load(2)->uuid(),
'url' => base_path() . 'taxonomy/term/2',
],
];
break;
case [3, 2]:
$expected_parent_normalization = [
[
'target_id' => 3,
'target_type' => 'taxonomy_term',
'target_uuid' => Term::load(3)->uuid(),
'url' => base_path() . 'taxonomy/term/3',
],
[
'target_id' => 2,
'target_type' => 'taxonomy_term',
'target_uuid' => Term::load(2)->uuid(),
'url' => base_path() . 'taxonomy/term/2',
],
];
break;
}
return [
'tid' => [
['value' => 1],
],
'revision_id' => [
['value' => 1],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'vid' => [
[
'target_id' => 'camelids',
'target_type' => 'taxonomy_vocabulary',
'target_uuid' => Vocabulary::load('camelids')->uuid(),
],
],
'name' => [
['value' => 'Llama'],
],
'description' => [
[
'value' => 'It is a little known fact that llamas cannot count higher than seven.',
'format' => NULL,
'processed' => "<p>It is a little known fact that llamas cannot count higher than seven.</p>\n",
],
],
'parent' => $expected_parent_normalization,
'weight' => [
['value' => 0],
],
'langcode' => [
[
'value' => 'en',
],
],
'changed' => [
[
'value' => (new \DateTime())->setTimestamp($this->entity->getChangedTime())->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339),
'format' => \DateTime::RFC3339,
],
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'path' => [
[
'alias' => '/llama',
'pid' => 1,
'langcode' => 'en',
],
],
'status' => [
[
'value' => TRUE,
],
],
'revision_created' => [
[
'value' => (new \DateTime())->setTimestamp((int) $this->entity->getRevisionCreationTime())
->setTimezone(new \DateTimeZone('UTC'))
->format(\DateTime::RFC3339),
'format' => \DateTime::RFC3339,
],
],
'revision_user' => [],
'revision_translation_affected' => [
[
'value' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'vid' => [
[
'target_id' => 'camelids',
],
],
'name' => [
[
'value' => 'Drama llama',
],
],
'description' => [
[
'value' => 'Drama llamas are the coolest camelids.',
'format' => NULL,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
switch ($method) {
case 'GET':
return "The 'access content' permission is required and the taxonomy term must be published.";
case 'POST':
return "The following permissions are required: 'create terms in camelids' OR 'administer taxonomy'.";
case 'PATCH':
return "The following permissions are required: 'edit terms in camelids' OR 'administer taxonomy'.";
case 'DELETE':
return "The following permissions are required: 'delete terms in camelids' OR 'administer taxonomy'.";
default:
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}
/**
* Tests PATCHing a term's path.
*
* For a negative test, see the similar test coverage for Node.
*
* @see \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase::testPatchPath()
*/
public function testPatchPath(): void {
$this->initAuthentication();
$this->provisionEntityResource();
$this->setUpAuthorization('GET');
$this->setUpAuthorization('PATCH');
$url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
// GET term's current normalization.
$response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
$normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
// Change term's path alias.
$normalization['path'][0]['alias'] .= 's-rule-the-world';
// Create term PATCH request.
$request_options = [];
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// PATCH request: 200.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
$updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
$this->assertSame($normalization['path'], $updated_normalization['path']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text', 'config:filter.settings']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
}
/**
* Tests GETting a term with a parent term other than the default <root> (0).
*
* @see ::getExpectedNormalizedEntity()
*
* @dataProvider providerTestGetTermWithParent
*/
public function testGetTermWithParent(array $parent_term_ids): void {
// Create all possible parent terms.
Term::create(['vid' => Vocabulary::load('camelids')->id()])
->setName('Lamoids')
->save();
Term::create(['vid' => Vocabulary::load('camelids')->id()])
->setName('Camels')
->save();
// Modify the entity under test to use the provided parent terms.
$this->entity->set('parent', $parent_term_ids)->save();
$this->initAuthentication();
$url = $this->getEntityResourceUrl();
$url->setOption('query', ['_format' => static::$format]);
$request_options = $this->getAuthenticationRequestOptions('GET');
$this->provisionEntityResource();
$this->setUpAuthorization('GET');
$response = $this->request('GET', $url, $request_options);
$expected = $this->getExpectedNormalizedEntity();
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
static::recursiveKSort($actual);
$this->assertSame($expected, $actual);
}
public static function providerTestGetTermWithParent() {
return [
'root parent: [0] (= no parent)' => [
[0],
],
'non-root parent: [2]' => [
[2],
],
'multiple parents: [0,2] (root + non-root parent)' => [
[0, 2],
],
'multiple parents: [3,2] (both non-root parents)' => [
[3, 2],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) {
// @see \Drupal\taxonomy\TermAccessControlHandler::checkAccess()
return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated)
->addCacheTags(['taxonomy_term:1']);
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
* @group #slow
*/
class TermXmlAnonTest extends TermResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
public function testPatchPath(): void {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
* @group #slow
*/
class TermXmlBasicAuthTest extends TermResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
/**
* {@inheritdoc}
*/
public function testPatchPath(): void {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
* @group #slow
*/
class TermXmlCookieTest extends TermResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
public function testPatchPath(): void {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class VocabularyJsonAnonTest extends VocabularyResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class VocabularyJsonBasicAuthTest extends VocabularyResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class VocabularyJsonCookieTest extends VocabularyResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rest\Functional\EntityResource\ConfigEntityResourceTestBase;
abstract class VocabularyResourceTestBase extends ConfigEntityResourceTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'taxonomy_vocabulary';
/**
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer taxonomy']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$vocabulary = Vocabulary::create([
'name' => 'Llama',
'vid' => 'llama',
]);
$vocabulary->save();
return $vocabulary;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'uuid' => $this->entity->uuid(),
'vid' => 'llama',
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [],
'name' => 'Llama',
'description' => NULL,
'weight' => 0,
'new_revision' => FALSE,
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
return [];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($method === 'GET') {
return "The following permissions are required: 'access taxonomy overview' OR 'administer taxonomy'.";
}
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class VocabularyXmlAnonTest extends VocabularyResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class VocabularyXmlBasicAuthTest extends VocabularyResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class VocabularyXmlCookieTest extends VocabularyResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\views\Views;
/**
* Tests the taxonomy RSS display.
*
* @group taxonomy
*/
class RssTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['node', 'field_ui', 'views'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* Name of the taxonomy term reference field.
*
* @var string
*/
protected $fieldName;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'bypass node access',
'administer content types',
'administer node display',
]));
$this->vocabulary = $this->createVocabulary();
$this->fieldName = 'taxonomy_' . $this->vocabulary->id();
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $this->fieldName, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($this->fieldName, [
'type' => 'options_select',
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($this->fieldName, [
'type' => 'entity_reference_label',
])
->save();
}
/**
* Tests that terms added to nodes are displayed in core RSS feed.
*
* Create a node and assert that taxonomy terms appear in rss.xml.
*/
public function testTaxonomyRss(): void {
// Create two taxonomy terms.
$term1 = $this->createTerm($this->vocabulary);
// Add the RSS display.
$default_display = $this->container->get('entity_display.repository')->getViewDisplay('node', 'article');
$rss_display = $default_display->createCopy('rss');
$rss_display->save();
// Change the format to 'RSS category'.
$rss_display->setComponent('taxonomy_' . $this->vocabulary->id(), [
'type' => 'entity_reference_rss_category',
'region' => 'content',
]);
$rss_display->save();
// Create an article.
$node = $this->drupalCreateNode([
'type' => 'article',
$this->fieldName => $term1->id(),
]);
// Check that the term is displayed when the RSS feed is viewed.
$this->drupalGet('rss.xml');
$test_element = sprintf(
'<category %s>%s</category>',
'domain="' . $term1->toUrl('canonical', ['absolute' => TRUE])->toString() . '"',
$term1->getName()
);
$this->assertSession()->responseContains($test_element);
// Test that the feed icon exists for the term.
$this->drupalGet("taxonomy/term/{$term1->id()}");
$this->assertSession()->linkByHrefExists("taxonomy/term/{$term1->id()}/feed");
// Test that the feed page exists for the term.
$this->drupalGet("taxonomy/term/{$term1->id()}/feed");
$assert = $this->assertSession();
$assert->responseHeaderContains('Content-Type', 'application/rss+xml');
// Ensure the RSS version is 2.0.
$rss_array = $this->getSession()->getDriver()->find('rss');
$this->assertEquals('2.0', reset($rss_array)->getAttribute('version'));
// Check that the "Exception value" is disabled by default.
$this->drupalGet('taxonomy/term/all/feed');
$this->assertSession()->statusCodeEquals(404);
// Set the exception value to 'all'.
$view = Views::getView('taxonomy_term');
$arguments = $view->getDisplay()->getOption('arguments');
$arguments['tid']['exception']['value'] = 'all';
$view->getDisplay()->overrideOption('arguments', $arguments);
$view->storage->save();
// Check the article is shown in the feed.
$raw_xml = '<title>' . $node->label() . '</title>';
$this->drupalGet('taxonomy/term/all/feed');
$this->assertSession()->responseContains($raw_xml);
// Unpublish the article and check that it is not shown in the feed.
$node->setUnpublished()->save();
$this->drupalGet('taxonomy/term/all/feed');
$this->assertSession()->responseNotContains($raw_xml);
}
}

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\taxonomy\VocabularyInterface;
/**
* Tests image upload on taxonomy terms.
*
* @group taxonomy
*/
class TaxonomyImageTest extends TaxonomyTestBase {
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
compareFiles as drupalCompareFiles;
}
/**
* The taxonomy vocabulary used for the test.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected VocabularyInterface $vocabulary;
/**
* {@inheritdoc}
*/
protected static $modules = ['image'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->vocabulary = $this->createVocabulary();
$entity_type = 'taxonomy_term';
$name = 'field_test';
FieldStorageConfig::create([
'field_name' => $name,
'entity_type' => $entity_type,
'type' => 'image',
])->save();
FieldConfig::create([
'field_name' => $name,
'entity_type' => $entity_type,
'bundle' => $this->vocabulary->id(),
'settings' => [],
])->save();
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay($entity_type, $this->vocabulary->id())
->setComponent($name, [
'type' => 'image_image',
'settings' => [],
])
->save();
}
/**
* Tests that a file can be uploaded before the taxonomy term has a name.
*/
public function testTaxonomyImageUpload(): void {
$user = $this->drupalCreateUser(['administer taxonomy']);
$this->drupalLogin($user);
$files = $this->drupalGetTestFiles('image');
$image = array_pop($files);
// Ensure that a file can be uploaded before taxonomy term has a name.
$edit = [
'files[field_test_0]' => \Drupal::service('file_system')->realpath($image->uri),
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->submitForm($edit, 'Upload');
$edit = [
'name[0][value]' => $this->randomMachineName(),
'field_test[0][alt]' => $this->randomMachineName(),
];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Created new term');
}
}

View File

@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* Taxonomy term revision delete form test.
*
* @group taxonomy
* @coversDefaultClass \Drupal\Core\Entity\Form\RevisionDeleteForm
*/
class TaxonomyRevisionDeleteTest extends BrowserTestBase {
use TaxonomyTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected $permissions = [
'view term revisions in test',
'delete all taxonomy revisions',
];
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
private $vocabulary;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->vocabulary = $this->createVocabulary(['vid' => 'test', 'name' => 'Test']);
}
/**
* Tests revision delete.
*/
public function testDeleteForm(): void {
$termName = $this->randomMachineName();
$entity = Term::create([
'vid' => $this->vocabulary->id(),
'name' => $termName,
]);
$entity->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 4pm'))->getTimestamp())
->setRevisionTranslationAffected(TRUE);
$entity->setNewRevision();
$entity->save();
$revisionId = $entity->getRevisionId();
$this->drupalLogin($this->drupalCreateUser($this->permissions));
// Cannot delete latest revision.
$this->drupalGet($entity->toUrl('revision-delete-form'));
$this->assertSession()->statusCodeEquals(403);
// Create a new latest revision.
$entity
->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 5pm'))->getTimestamp())
->setRevisionTranslationAffected(TRUE)
->setNewRevision();
$entity->save();
// Reload the entity.
$revision = \Drupal::entityTypeManager()->getStorage('taxonomy_term')
->loadRevision($revisionId);
$this->drupalGet($revision->toUrl('revision-delete-form'));
$this->assertSession()->pageTextContains('Are you sure you want to delete the revision from Sun, 01/11/2009 - 16:00?');
$this->assertSession()->buttonExists('Delete');
$this->assertSession()->linkExists('Cancel');
$countRevisions = static function (): int {
return (int) \Drupal::entityTypeManager()->getStorage('taxonomy_term')
->getQuery()
->accessCheck(FALSE)
->allRevisions()
->count()
->execute();
};
$count = $countRevisions();
$this->submitForm([], 'Delete');
$this->assertEquals($count - 1, $countRevisions());
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->addressEquals(sprintf('taxonomy/term/%s/revisions', $entity->id()));
$this->assertSession()->pageTextContains(sprintf('Revision from Sun, 01/11/2009 - 16:00 of Test %s has been deleted.', $termName));
$this->assertSession()->elementsCount('css', 'table tbody tr', 1);
}
}

View File

@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* Taxonomy term revision form test.
*
* @group taxonomy
* @coversDefaultClass \Drupal\Core\Entity\Form\RevisionRevertForm
*/
class TaxonomyRevisionRevertTest extends BrowserTestBase {
use TaxonomyTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected $permissions = [
'view term revisions in test',
'revert all taxonomy revisions',
];
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
private $vocabulary;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->vocabulary = $this->createVocabulary(['vid' => 'test', 'name' => 'Test']);
}
/**
* Tests revision revert.
*/
public function testRevertForm(): void {
$termName = $this->randomMachineName();
$entity = Term::create([
'vid' => $this->vocabulary->id(),
'name' => $termName,
]);
$entity->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 4pm'))->getTimestamp())
->setRevisionTranslationAffected(TRUE);
$entity->setNewRevision();
$entity->save();
$revisionId = $entity->getRevisionId();
$this->drupalLogin($this->drupalCreateUser($this->permissions));
// Cannot revert latest revision.
$this->drupalGet($entity->toUrl('revision-revert-form'));
$this->assertSession()->statusCodeEquals(403);
// Create a new latest revision.
$entity
->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 5pm'))->getTimestamp())
->setRevisionTranslationAffected(TRUE)
->setNewRevision();
$entity->save();
// Reload the entity.
$revision = \Drupal::entityTypeManager()->getStorage('taxonomy_term')
->loadRevision($revisionId);
$this->drupalGet($revision->toUrl('revision-revert-form'));
$this->assertSession()->pageTextContains('Are you sure you want to revert to the revision from Sun, 01/11/2009 - 16:00?');
$this->assertSession()->buttonExists('Revert');
$this->assertSession()->linkExists('Cancel');
$countRevisions = static function (): int {
return (int) \Drupal::entityTypeManager()->getStorage('taxonomy_term')
->getQuery()
->accessCheck(FALSE)
->allRevisions()
->count()
->execute();
};
$count = $countRevisions();
$this->submitForm([], 'Revert');
$this->assertEquals($count + 1, $countRevisions());
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->addressEquals(sprintf('taxonomy/term/%s/revisions', $entity->id()));
$this->assertSession()->pageTextContains(sprintf('Test %s has been reverted to the revision from Sun, 01/11/2009 - 16:00.', $termName));
$this->assertSession()->elementsCount('css', 'table tbody tr', 3);
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* Tests the new_revision setting of taxonomy vocabularies.
*
* @group taxonomy
*/
class TaxonomyRevisionTest extends BrowserTestBase {
use TaxonomyTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests default revision settings on vocabularies.
*/
public function testVocabularyTermRevision(): void {
$assert = $this->assertSession();
$vocabulary1 = $this->createVocabulary(['new_revision' => TRUE]);
$vocabulary2 = $this->createVocabulary(['new_revision' => FALSE]);
$user = $this->createUser([
'administer taxonomy',
]);
$term1 = $this->createTerm($vocabulary1);
$term2 = $this->createTerm($vocabulary2);
// Create some revisions so revision checkbox is visible.
$term1 = $this->createTaxonomyTermRevision($term1);
$term2 = $this->createTaxonomyTermRevision($term2);
$this->drupalLogin($user);
$this->drupalGet($term1->toUrl('edit-form'));
$assert->statusCodeEquals(200);
$assert->checkboxChecked('Create new revision');
$this->drupalGet($term2->toUrl('edit-form'));
$assert->statusCodeEquals(200);
$assert->checkboxNotChecked('Create new revision');
}
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* Taxonomy term version history test.
*
* @group taxonomy
* @coversDefaultClass \Drupal\Core\Entity\Controller\VersionHistoryController
*/
class TaxonomyRevisionVersionHistoryTest extends BrowserTestBase {
use TaxonomyTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected $permissions = [
'view term revisions in test',
'revert all taxonomy revisions',
'delete all taxonomy revisions',
];
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
private $vocabulary;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->vocabulary = $this->createVocabulary(['vid' => 'test', 'name' => 'Test']);
}
/**
* Tests version history page.
*/
public function testVersionHistory(): void {
$entity = Term::create([
'vid' => $this->vocabulary->id(),
'name' => 'Test taxonomy term',
]);
$entity
->setDescription('Description 1')
->setRevisionCreationTime((new \DateTimeImmutable('1st June 2020 7am'))->getTimestamp())
->setRevisionLogMessage('first revision log')
->setRevisionUser($this->drupalCreateUser(name: 'first author'))
->setNewRevision();
$entity->save();
$entity
->setDescription('Description 2')
->setRevisionCreationTime((new \DateTimeImmutable('2nd June 2020 8am'))->getTimestamp())
->setRevisionLogMessage('second revision log')
->setRevisionUser($this->drupalCreateUser(name: 'second author'))
->setNewRevision();
$entity->save();
$entity
->setDescription('Description 3')
->setRevisionCreationTime((new \DateTimeImmutable('3rd June 2020 9am'))->getTimestamp())
->setRevisionLogMessage('third revision log')
->setRevisionUser($this->drupalCreateUser(name: 'third author'))
->setNewRevision();
$entity->save();
$this->drupalLogin($this->drupalCreateUser($this->permissions));
$this->drupalGet($entity->toUrl('version-history'));
$this->assertSession()->elementsCount('css', 'table tbody tr', 3);
// Order is newest to oldest revision by creation order.
$row1 = $this->assertSession()->elementExists('css', 'table tbody tr:nth-child(1)');
// Latest revision does not have revert or delete revision operation.
$this->assertSession()->elementNotExists('named', ['link', 'Revert'], $row1);
$this->assertSession()->elementNotExists('named', ['link', 'Delete'], $row1);
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(1)', 'Current revision');
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(1)', 'third revision log');
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(1)', '06/03/2020 - 09:00 by third author');
$row2 = $this->assertSession()->elementExists('css', 'table tbody tr:nth-child(2)');
$this->assertSession()->elementExists('named', ['link', 'Revert'], $row2);
$this->assertSession()->elementExists('named', ['link', 'Delete'], $row2);
$this->assertSession()->elementTextNotContains('css', 'table tbody tr:nth-child(2)', 'Current revision');
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(2)', 'second revision log');
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(2)', '06/02/2020 - 08:00 by second author');
$row3 = $this->assertSession()->elementExists('css', 'table tbody tr:nth-child(3)');
$this->assertSession()->elementExists('named', ['link', 'Revert'], $row3);
$this->assertSession()->elementExists('named', ['link', 'Delete'], $row3);
$this->assertSession()->elementTextNotContains('css', 'table tbody tr:nth-child(2)', 'Current revision');
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(3)', 'first revision log');
$this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(3)', '06/01/2020 - 07:00 by first author');
}
}

View File

@@ -0,0 +1,217 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Url;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
use Drupal\workflows\Entity\Workflow;
/**
* Tests taxonomy terms with Content Moderation.
*
* @group content_moderation
* @group taxonomy
*/
class TaxonomyTermContentModerationTest extends TaxonomyTestBase {
use ContentModerationTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The vocabulary used for creating terms.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* {@inheritdoc}
*/
protected static $modules = ['content_moderation'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->createEditorialWorkflow();
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'use editorial transition create_new_draft',
'use editorial transition publish',
'view any unpublished content',
'view latest version',
]));
$this->vocabulary = $this->createVocabulary();
// Set the vocabulary as moderated.
$workflow = Workflow::load('editorial');
$workflow->getTypePlugin()->addEntityTypeAndBundle('taxonomy_term', $this->vocabulary->id());
$workflow->save();
}
/**
* Tests taxonomy term parents on a moderated vocabulary.
*/
public function testTaxonomyTermParents(): void {
$assert_session = $this->assertSession();
// Create a simple hierarchy in the vocabulary, a root term and three parent
// terms.
$root = $this->createTerm($this->vocabulary, ['langcode' => 'en', 'moderation_state' => 'published']);
$parent_1 = $this->createTerm($this->vocabulary, [
'langcode' => 'en',
'moderation_state' => 'published',
'parent' => $root->id(),
]);
$parent_2 = $this->createTerm($this->vocabulary, [
'langcode' => 'en',
'moderation_state' => 'published',
'parent' => $root->id(),
]);
$parent_3 = $this->createTerm($this->vocabulary, [
'langcode' => 'en',
'moderation_state' => 'published',
'parent' => $root->id(),
]);
// Create a child term and assign one of the parents above.
$child = $this->createTerm($this->vocabulary, [
'langcode' => 'en',
'moderation_state' => 'published',
'parent' => $parent_1->id(),
]);
/** @var \Drupal\taxonomy\TermStorageInterface $taxonomy_storage */
$taxonomy_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$validation_message = 'You can only change the hierarchy for the published version of this term.';
// Add a pending revision without changing the term parent.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['moderation_state[0][state]' => 'draft'], 'Save');
$assert_session->pageTextNotContains($validation_message);
// Add a pending revision and change the parent.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['parent[]' => [$parent_2->id()], 'moderation_state[0][state]' => 'draft'], 'Save');
// Check that parents were not changed.
$assert_session->pageTextContains($validation_message);
$taxonomy_storage->resetCache();
$this->assertEquals([$parent_1->id()], array_keys($taxonomy_storage->loadParents($child->id())));
// Add a pending revision and add a new parent.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['parent[]' => [$parent_1->id(), $parent_3->id()], 'moderation_state[0][state]' => 'draft'], 'Save');
// Check that parents were not changed.
$assert_session->pageTextContains($validation_message);
$taxonomy_storage->resetCache();
$this->assertEquals([$parent_1->id()], array_keys($taxonomy_storage->loadParents($child->id())));
// Add a pending revision and use the root term as a parent.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['parent[]' => [$root->id()], 'moderation_state[0][state]' => 'draft'], 'Save');
// Check that parents were not changed.
$assert_session->pageTextContains($validation_message);
$taxonomy_storage->resetCache();
$this->assertEquals([$parent_1->id()], array_keys($taxonomy_storage->loadParents($child->id())));
// Add a pending revision and remove the parent.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['parent[]' => [], 'moderation_state[0][state]' => 'draft'], 'Save');
// Check that parents were not changed.
$assert_session->pageTextContains($validation_message);
$taxonomy_storage->resetCache();
$this->assertEquals([$parent_1->id()], array_keys($taxonomy_storage->loadParents($child->id())));
// Add a published revision.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['moderation_state[0][state]' => 'published'], 'Save');
// Change the parents.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['parent[]' => [$parent_2->id()]], 'Save');
// Check that parents were changed.
$assert_session->pageTextNotContains($validation_message);
$taxonomy_storage->resetCache();
$this->assertNotEquals([$parent_1->id()], array_keys($taxonomy_storage->loadParents($child->id())));
// Add a pending revision and change the weight.
$this->drupalGet($child->toUrl('edit-form'));
$this->submitForm(['weight' => 10, 'moderation_state[0][state]' => 'draft'], 'Save');
// Check that weight was not changed.
$assert_session->pageTextContains($validation_message);
// Add a new term without any parent and publish it.
$edit = [
'name[0][value]' => $this->randomMachineName(),
'moderation_state[0][state]' => 'published',
];
$this->drupalGet(Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $this->vocabulary->id()]));
$this->submitForm($edit, 'Save');
// Add a pending revision without any changes.
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['name' => $edit['name[0][value]']]);
$term = reset($terms);
$this->drupalGet($term->toUrl('edit-form'));
$this->submitForm(['moderation_state[0][state]' => 'draft'], 'Save');
$assert_session->pageTextNotContains($validation_message);
}
/**
* Tests changing field values in pending revisions of taxonomy terms.
*/
public function testTaxonomyTermPendingRevisions(): void {
$assert_session = $this->assertSession();
$default_term_name = 'term - default revision';
$default_term_description = 'The default revision of a term.';
$term = $this->createTerm($this->vocabulary, [
'name' => $default_term_name,
'description' => $default_term_description,
'langcode' => 'en',
'moderation_state' => 'published',
]);
// Add a pending revision without changing the term parent.
$this->drupalGet($term->toUrl('edit-form'));
$assert_session->pageTextContains($default_term_name);
$assert_session->pageTextContains($default_term_description);
// Check the revision log message field appears on the term edit page.
$this->drupalGet($term->toUrl('edit-form'));
$assert_session->fieldExists('revision_log_message[0][value]');
$pending_term_name = 'term - pending revision';
$pending_term_description = 'The pending revision of a term.';
$edit = [
'name[0][value]' => $pending_term_name,
'description[0][value]' => $pending_term_description,
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet($term->toUrl('edit-form'));
$this->submitForm($edit, 'Save');
$assert_session->pageTextContains($pending_term_name);
$assert_session->pageTextContains($pending_term_description);
$assert_session->pageTextNotContains($default_term_description);
// Check that the default revision of the term contains the correct values.
$this->drupalGet('taxonomy/term/' . $term->id());
$assert_session->pageTextContains($default_term_name);
$assert_session->pageTextContains($default_term_description);
}
}

View File

@@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
/**
* Ensure that the term indentation works properly.
*
* @group taxonomy
*/
class TaxonomyTermIndentationTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'bypass node access',
]));
$this->vocabulary = $this->createVocabulary();
}
/**
* Tests term indentation.
*/
public function testTermIndentation(): void {
$assert = $this->assertSession();
// Create three taxonomy terms.
$term1 = $this->createTerm($this->vocabulary);
$term2 = $this->createTerm($this->vocabulary);
$term3 = $this->createTerm($this->vocabulary);
// Get the taxonomy storage.
$taxonomy_storage = $this->container->get('entity_type.manager')->getStorage('taxonomy_term');
// Indent the second term under the first one.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->get('vid') . '/overview');
$hidden_edit = [
'terms[tid:' . $term2->id() . ':0][term][tid]' => 2,
'terms[tid:' . $term2->id() . ':0][term][parent]' => 1,
'terms[tid:' . $term2->id() . ':0][term][depth]' => 1,
];
// Because we can't post hidden form elements, we have to change them in
// code here, and then submit.
foreach ($hidden_edit as $field => $value) {
$node = $assert->hiddenFieldExists($field);
$node->setValue($value);
}
$edit = [
'terms[tid:' . $term2->id() . ':0][weight]' => 1,
];
// Submit the edited form and check for HTML indentation element presence.
$this->submitForm($edit, 'Save');
$this->assertSession()->responseMatches('|<div class="js-indentation indentation">&nbsp;</div>|');
// Check explicitly that term 2's parent is term 1.
$parents = $taxonomy_storage->loadParents($term2->id());
$this->assertEquals(1, key($parents), 'Term 1 is the term 2\'s parent');
// Move the second term back out to the root level.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->get('vid') . '/overview');
$hidden_edit = [
'terms[tid:' . $term2->id() . ':0][term][tid]' => 2,
'terms[tid:' . $term2->id() . ':0][term][parent]' => 0,
'terms[tid:' . $term2->id() . ':0][term][depth]' => 0,
];
// Because we can't post hidden form elements, we have to change them in
// code here, and then submit.
foreach ($hidden_edit as $field => $value) {
$node = $assert->hiddenFieldExists($field);
$node->setValue($value);
}
$edit = [
'terms[tid:' . $term2->id() . ':0][weight]' => 1,
];
$this->submitForm($edit, 'Save');
// All terms back at the root level, no indentation should be present.
$this->assertSession()->responseNotMatches('|<div class="js-indentation indentation">&nbsp;</div>|');
// Check explicitly that term 2 has no parents.
\Drupal::entityTypeManager()->getStorage('taxonomy_term')->resetCache();
$parents = $taxonomy_storage->loadParents($term2->id());
$this->assertEmpty($parents, 'Term 2 has no parents now');
}
}

View File

@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
/**
* Ensures that the term pager works properly.
*
* @group taxonomy
*/
class TaxonomyTermPagerTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy', 'taxonomy_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'bypass node access',
]));
$this->vocabulary = $this->createVocabulary();
}
/**
* Tests that the pager is displayed properly on the term overview page.
*/
public function testTaxonomyTermOverviewPager(): void {
// Set limit to 3 terms per page.
$this->config('taxonomy.settings')
->set('terms_per_page_admin', '3')
->save();
// Create 3 terms.
for ($x = 1; $x <= 3; $x++) {
$this->createTerm($this->vocabulary);
}
// Get Page 1.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
// Pager should not be visible.
$this->assertSession()->responseNotMatches('|<nav class="pager" [^>]*>|');
// Create 3 more terms to show pager.
for ($x = 1; $x <= 3; $x++) {
$this->createTerm($this->vocabulary);
}
// Ensure that pager is visible on page 1.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->responseMatches('|<nav class="pager" [^>]*>|');
// Ensure that pager is visible on page 2.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', ['query' => ['page' => 1]]);
$this->assertSession()->responseMatches('|<nav class="pager" [^>]*>|');
}
/**
* Tests that overview page only loads the necessary terms.
*/
public function testTaxonomyTermOverviewTermLoad(): void {
// Set limit to 3 terms per page.
$this->config('taxonomy.settings')
->set('terms_per_page_admin', '3')
->save();
$state = $this->container->get('state');
// Create 5 terms.
for ($x = 0; $x <= 10; $x++) {
$this->createTerm($this->vocabulary, ['weight' => $x]);
}
// Check the overview page.
$state->set('taxonomy_test_taxonomy_term_load', []);
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$loaded_terms = $state->get('taxonomy_test_taxonomy_term_load');
$this->assertCount(4, $loaded_terms);
// Check the overview page for submit callback.
$state->set('taxonomy_test_taxonomy_term_load', []);
$this->submitForm([], 'Save');
$loaded_terms = $state->get('taxonomy_test_taxonomy_term_load');
$this->assertCount(4, $loaded_terms);
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', ['query' => ['page' => 2]]);
$state->set('taxonomy_test_taxonomy_term_load', []);
$this->submitForm([], 'Save');
$loaded_terms = $state->get('taxonomy_test_taxonomy_term_load');
$this->assertCount(4, $loaded_terms);
// Adding a new term with weight < 0 implies that all root terms are updated.
$this->createTerm($this->vocabulary, ['weight' => -1]);
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', ['query' => ['page' => 2]]);
$state->set('taxonomy_test_taxonomy_term_load', []);
$this->submitForm([], 'Save');
$loaded_terms = $state->get('taxonomy_test_taxonomy_term_load');
$this->assertCount(12, $loaded_terms);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* Provides common helper methods for Taxonomy module tests.
*/
abstract class TaxonomyTestBase extends BrowserTestBase {
use TaxonomyTestTrait;
use EntityReferenceFieldCreationTrait;
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy', 'block'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
// Create Basic page and Article node types.
if ($this->profile != 'standard') {
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
/**
* Provides common testing base for translated taxonomy terms.
*/
trait TaxonomyTranslationTestTrait {
use EntityReferenceFieldCreationTrait;
/**
* The vocabulary.
*
* @var \Drupal\taxonomy\Entity\Vocabulary
*/
protected $vocabulary;
/**
* The field name for our taxonomy term field.
*
* @var string
*/
protected $termFieldName = 'field_tag';
/**
* The langcode of the source language.
*
* @var string
*/
protected $baseLangcode = 'en';
/**
* Target langcode for translation.
*
* @var string
*/
protected $translateToLangcode = 'hu';
/**
* The node to check the translated value on.
*
* @var \Drupal\node\Entity\Node
*/
protected $node;
/**
* Adds additional languages.
*/
protected function setupLanguages() {
ConfigurableLanguage::createFromLangcode($this->translateToLangcode)->save();
$this->rebuildContainer();
}
/**
* Enables translations where it needed.
*/
protected function enableTranslation() {
// Enable translation for the current entity type and ensure the change is
// picked up.
\Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
\Drupal::service('content_translation.manager')->setEnabled('taxonomy_term', $this->vocabulary->id(), TRUE);
}
/**
* Adds term reference field for the article content type.
*/
protected function setUpTermReferenceField() {
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $this->termFieldName, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$field_storage = FieldStorageConfig::loadByName('node', $this->termFieldName);
$field_storage->setTranslatable(FALSE);
$field_storage->save();
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($this->termFieldName, [
'type' => 'entity_reference_autocomplete_tags',
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($this->termFieldName, [
'type' => 'entity_reference_label',
])
->save();
}
}

View File

@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\TermInterface;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Tests the taxonomy term access permissions.
*
* @group taxonomy
*/
class TermAccessTest extends TaxonomyTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests access control functionality for taxonomy terms.
*/
public function testTermAccess(): void {
$assert_session = $this->assertSession();
$vocabulary = $this->createVocabulary();
// Create two terms.
$published_term = Term::create([
'vid' => $vocabulary->id(),
'name' => 'Published term',
'status' => 1,
]);
$published_term->save();
$unpublished_term = Term::create([
'vid' => $vocabulary->id(),
'name' => 'Unpublished term',
'status' => 0,
]);
$unpublished_term->save();
// Start off logged in as admin.
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
// Test the 'administer taxonomy' permission.
$this->drupalGet('taxonomy/term/' . $published_term->id());
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($published_term, 'view', TRUE);
$this->drupalGet('taxonomy/term/' . $unpublished_term->id());
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($unpublished_term, 'view', TRUE);
$this->drupalGet('taxonomy/term/' . $published_term->id() . '/edit');
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($published_term, 'update', TRUE);
$this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/edit');
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($unpublished_term, 'update', TRUE);
$this->drupalGet('taxonomy/term/' . $published_term->id() . '/delete');
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($published_term, 'delete', TRUE);
$this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/delete');
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($unpublished_term, 'delete', TRUE);
// Test the 'access content' permission.
$this->drupalLogin($this->drupalCreateUser(['access content']));
$this->drupalGet('taxonomy/term/' . $published_term->id());
$assert_session->statusCodeEquals(200);
$this->assertTermAccess($published_term, 'view', TRUE);
$this->drupalGet('taxonomy/term/' . $unpublished_term->id());
$assert_session->statusCodeEquals(403);
$this->assertTermAccess($unpublished_term, 'view', FALSE, "The 'access content' permission is required and the taxonomy term must be published.");
$this->drupalGet('taxonomy/term/' . $published_term->id() . '/edit');
$assert_session->statusCodeEquals(403);
$this->assertTermAccess($published_term, 'update', FALSE, "The following permissions are required: 'edit terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
$this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/edit');
$assert_session->statusCodeEquals(403);
$this->assertTermAccess($unpublished_term, 'update', FALSE, "The following permissions are required: 'edit terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
$this->drupalGet('taxonomy/term/' . $published_term->id() . '/delete');
$assert_session->statusCodeEquals(403);
$this->assertTermAccess($published_term, 'delete', FALSE, "The following permissions are required: 'delete terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
$this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/delete');
$assert_session->statusCodeEquals(403);
$this->assertTermAccess($unpublished_term, 'delete', FALSE, "The following permissions are required: 'delete terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
// Install the Views module and repeat the checks for the 'view' permission.
\Drupal::service('module_installer')->install(['views'], TRUE);
$this->rebuildContainer();
$this->drupalGet('taxonomy/term/' . $published_term->id());
$assert_session->statusCodeEquals(200);
// @todo Change this assertion to expect a 403 status code when
// https://www.drupal.org/project/drupal/issues/2983070 is fixed.
$this->drupalGet('taxonomy/term/' . $unpublished_term->id());
$assert_session->statusCodeEquals(404);
}
/**
* Checks access on taxonomy term.
*
* @param \Drupal\taxonomy\TermInterface $term
* A taxonomy term entity.
* @param string $access_operation
* The entity operation, e.g. 'view', 'edit', 'delete', etc.
* @param bool $access_allowed
* Whether the current use has access to the given operation or not.
* @param string $access_reason
* (optional) The reason of the access result.
*
* @internal
*/
protected function assertTermAccess(TermInterface $term, string $access_operation, bool $access_allowed, string $access_reason = ''): void {
$access_result = $term->access($access_operation, NULL, TRUE);
$this->assertSame($access_allowed, $access_result->isAllowed());
if ($access_reason) {
$this->assertSame($access_reason, $access_result->getReason());
}
}
}

View File

@@ -0,0 +1,236 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests the autocomplete implementation of the taxonomy class.
*
* @group taxonomy
*/
class TermAutocompleteTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['node'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The vocabulary.
*
* @var \Drupal\taxonomy\Entity\Vocabulary
*/
protected $vocabulary;
/**
* The field to add to the content type for the taxonomy terms.
*
* @var string
*/
protected $fieldName;
/**
* The admin user.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* The autocomplete URL to call.
*
* @var string
*/
protected $autocompleteUrl;
/**
* The term IDs indexed by term names.
*
* @var array
*/
protected $termIds;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create a vocabulary.
$this->vocabulary = $this->createVocabulary();
// Create 11 terms, which have some sub-string in common, in a
// non-alphabetical order, so that we will have more than 10 matches later
// when we test the correct number of results is returned, and we can test
// the order of the results. The location of the sub-string to match varies
// also, since it should not be necessary to start with the sub-string to
// match it. Save term IDs to reuse later.
$termNames = [
'aaa 20 bbb',
'aaa 70 bbb',
'aaa 10 bbb',
'aaa 12 bbb',
'aaa 40 bbb',
'aaa 11 bbb',
'aaa 30 bbb',
'aaa 50 bbb',
'aaa 80',
'aaa 90',
'bbb 60 aaa',
];
foreach ($termNames as $termName) {
$term = $this->createTerm($this->vocabulary, ['name' => $termName]);
$this->termIds[$termName] = $term->id();
}
// Create a taxonomy_term_reference field on the article Content Type that
// uses a taxonomy_autocomplete widget.
$this->fieldName = $this->randomMachineName();
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'entity_type' => 'node',
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'taxonomy_term',
],
])->save();
FieldConfig::create([
'field_name' => $this->fieldName,
'bundle' => 'article',
'entity_type' => 'node',
'settings' => [
'handler' => 'default',
'handler_settings' => [
// Restrict selection of terms to a single vocabulary.
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
],
],
])->save();
EntityFormDisplay::load('node.article.default')
->setComponent($this->fieldName, [
'type' => 'entity_reference_autocomplete',
])
->save();
EntityViewDisplay::load('node.article.default')
->setComponent($this->fieldName, [
'type' => 'entity_reference_label',
])
->save();
// Create a user and then login.
$this->adminUser = $this->drupalCreateUser(['create article content']);
$this->drupalLogin($this->adminUser);
// Retrieve the autocomplete URL.
$this->drupalGet('node/add/article');
$field = $this->assertSession()->fieldExists("{$this->fieldName}[0][target_id]");
$this->autocompleteUrl = $this->getAbsoluteUrl($field->getAttribute('data-autocomplete-path'));
}
/**
* Helper function for JSON formatted requests.
*
* @param string|\Drupal\Core\Url $path
* Drupal path or URL to load into Mink controlled browser.
* @param array $options
* (optional) Options to be forwarded to the URL generator.
* @param string[] $headers
* (optional) An array containing additional HTTP request headers.
*
* @return string[]
* Array representing decoded JSON response.
*/
protected function drupalGetJson($path, array $options = [], array $headers = []) {
$options = array_merge_recursive(['query' => ['_format' => 'json']], $options);
return Json::decode($this->drupalGet($path, $options, $headers));
}
/**
* Tests that the autocomplete method returns the good number of results.
*
* @see \Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete()
*/
public function testAutocompleteCountResults(): void {
// Test that no matching term found.
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'zzz']]
);
$this->assertEmpty($data, 'Autocomplete returned no results');
// Test that only one matching term found, when only one matches.
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'aaa 10']]
);
$this->assertCount(1, $data, 'Autocomplete returned 1 result');
// Test the correct number of matches when multiple are partial matches.
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'aaa 1']]
);
$this->assertCount(3, $data, 'Autocomplete returned 3 results');
// Tests that only 10 results are returned, even if there are more than 10
// matches.
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'aaa']]
);
$this->assertCount(10, $data, 'Autocomplete returned only 10 results (for over 10 matches)');
}
/**
* Tests that the autocomplete method returns properly ordered results.
*
* @see \Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete()
*/
public function testAutocompleteOrderedResults(): void {
$expectedResults = [
'aaa 10 bbb',
'aaa 11 bbb',
'aaa 12 bbb',
'aaa 20 bbb',
'aaa 30 bbb',
'aaa 40 bbb',
'aaa 50 bbb',
'aaa 70 bbb',
'bbb 60 aaa',
];
// Build $expected to match the autocomplete results.
$expected = [];
foreach ($expectedResults as $termName) {
$expected[] = [
'value' => $termName . ' (' . $this->termIds[$termName] . ')',
'label' => $termName,
];
}
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'bbb']]
);
$this->assertSame($expected, $data);
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase;
/**
* Tests the Taxonomy term entity's cache tags.
*
* @group taxonomy
*/
class TermCacheTagsTest extends EntityWithUriCacheTagsTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Camelids" vocabulary.
$vocabulary = Vocabulary::create([
'name' => 'Camelids',
'vid' => 'camelids',
]);
$vocabulary->save();
// Create a "Llama" taxonomy term.
$term = Term::create([
'name' => 'Llama',
'vid' => $vocabulary->id(),
]);
$term->save();
return $term;
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
/**
* Tests views contextual links on terms.
*
* @group taxonomy
*/
class TermContextualLinksTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'contextual',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests contextual links.
*/
public function testTermContextualLinks(): void {
$vocabulary = $this->createVocabulary();
$term = $this->createTerm($vocabulary);
$user = $this->drupalCreateUser([
'administer taxonomy',
'access contextual links',
]);
$this->drupalLogin($user);
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->elementExists('css', 'div[data-contextual-id^="taxonomy_term:taxonomy_term=' . $term->id() . ':"]');
}
}

View File

@@ -0,0 +1,259 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Link;
use Drupal\Core\Database\Database;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Tests the hook implementations that maintain the taxonomy index.
*
* @group taxonomy
*/
class TermIndexTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['views'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* Name of the taxonomy term reference field.
*
* @var string
*/
protected $fieldName1;
/**
* Name of the taxonomy term reference field.
*
* @var string
*/
protected $fieldName2;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create an administrative user.
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'bypass node access',
]));
// Create a vocabulary and add two term reference fields to article nodes.
$this->vocabulary = $this->createVocabulary();
$this->fieldName1 = $this->randomMachineName();
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $this->fieldName1, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($this->fieldName1, [
'type' => 'options_select',
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($this->fieldName1, [
'type' => 'entity_reference_label',
])
->save();
$this->fieldName2 = $this->randomMachineName();
$this->createEntityReferenceField('node', 'article', $this->fieldName2, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($this->fieldName2, [
'type' => 'options_select',
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($this->fieldName2, [
'type' => 'entity_reference_label',
])
->save();
}
/**
* Tests that the taxonomy index is maintained properly.
*/
public function testTaxonomyIndex(): void {
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
// Create terms in the vocabulary.
$term_1 = $this->createTerm($this->vocabulary);
$term_2 = $this->createTerm($this->vocabulary);
// Post an article.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['body[0][value]'] = $this->randomMachineName();
$edit["{$this->fieldName1}[]"] = $term_1->id();
$edit["{$this->fieldName2}[]"] = $term_1->id();
$this->drupalGet('node/add/article');
$this->submitForm($edit, 'Save');
// Check that the term is indexed, and only once.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$connection = Database::getConnection();
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_1->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 1 is indexed once.');
// Update the article to change one term.
$edit["{$this->fieldName1}[]"] = $term_2->id();
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check that both terms are indexed.
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_1->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 1 is indexed.');
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_2->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 2 is indexed.');
// Update the article to change another term.
$edit["{$this->fieldName2}[]"] = $term_2->id();
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check that only one term is indexed.
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_1->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(0, $index_count, 'Term 1 is not indexed.');
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_2->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 2 is indexed once.');
// Redo the above tests without interface.
$node_storage->resetCache([$node->id()]);
$node = $node_storage->load($node->id());
$node->title = $this->randomMachineName();
// Update the article with no term changed.
$node->save();
// Check that the index was not changed.
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_1->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(0, $index_count, 'Term 1 is not indexed.');
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_2->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 2 is indexed once.');
// Update the article to change one term.
$node->{$this->fieldName1} = [['target_id' => $term_1->id()]];
$node->save();
// Check that both terms are indexed.
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_1->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 1 is indexed.');
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_2->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 2 is indexed.');
// Update the article to change another term.
$node->{$this->fieldName2} = [['target_id' => $term_1->id()]];
$node->save();
// Check that only one term is indexed.
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_1->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(1, $index_count, 'Term 1 is indexed once.');
$index_count = $connection->select('taxonomy_index')
->condition('nid', $node->id())
->condition('tid', $term_2->id())
->countQuery()
->execute()
->fetchField();
$this->assertEquals(0, $index_count, 'Term 2 is not indexed.');
}
/**
* Tests that there is a link to the parent term on the child term page.
*/
public function testTaxonomyTermHierarchyBreadcrumbs(): void {
// Create two taxonomy terms and set term2 as the parent of term1.
$term1 = $this->createTerm($this->vocabulary);
$term2 = $this->createTerm($this->vocabulary);
$term1->parent = [$term2->id()];
$term1->save();
// Verify that the page breadcrumbs include a link to the parent term.
$this->drupalGet('taxonomy/term/' . $term1->id());
// Breadcrumbs are not rendered with a language, prevent the term
// language from being added to the options.
// Check that parent term link is displayed when viewing the node.
$this->assertSession()->responseContains(Link::fromTextAndUrl($term2->getName(), $term2->toUrl('canonical', ['language' => NULL]))->toString());
}
}

View File

@@ -0,0 +1,163 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Language\LanguageInterface;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the language functionality for the taxonomy terms.
*
* @group taxonomy
*/
class TermLanguageTest extends TaxonomyTestBase {
protected static $modules = ['language'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create an administrative user.
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
// Create a vocabulary to which the terms will be assigned.
$this->vocabulary = $this->createVocabulary();
// Add some custom languages.
foreach (['aa', 'bb', 'cc'] as $language_code) {
ConfigurableLanguage::create([
'id' => $language_code,
'label' => $this->randomMachineName(),
])->save();
}
}
public function testTermLanguage(): void {
// Configure the vocabulary to not hide the language selector.
$edit = [
'default_language[language_alterable]' => TRUE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id());
$this->submitForm($edit, 'Save');
// Add a term.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
// Check that we have the language selector.
$this->assertSession()->fieldExists('edit-langcode-0-value');
// Submit the term.
$edit = [
'name[0][value]' => $this->randomMachineName(),
'langcode[0][value]' => 'aa',
];
$this->submitForm($edit, 'Save');
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => $edit['name[0][value]'],
]);
$term = reset($terms);
$this->assertEquals($edit['langcode[0][value]'], $term->language()->getId(), 'The term contains the correct langcode.');
// Check if on the edit page the language is correct.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', $edit['langcode[0][value]'])->isSelected());
// Change the language of the term.
$edit['langcode[0][value]'] = 'bb';
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check again that on the edit page the language is correct.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', $edit['langcode[0][value]'])->isSelected());
}
public function testDefaultTermLanguage(): void {
// Configure the vocabulary to not hide the language selector, and make the
// default language of the terms fixed.
$edit = [
'default_language[langcode]' => 'bb',
'default_language[language_alterable]' => TRUE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id());
$this->submitForm($edit, 'Save');
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', 'bb')->isSelected());
// Make the default language of the terms to be the current interface.
$edit = [
'default_language[langcode]' => 'current_interface',
'default_language[language_alterable]' => TRUE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id());
$this->submitForm($edit, 'Save');
$this->drupalGet('aa/admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', 'aa')->isSelected());
$this->drupalGet('bb/admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', 'bb')->isSelected());
// Change the default language of the site and check if the default terms
// language is still correctly selected.
$this->config('system.site')->set('default_langcode', 'cc')->save();
$edit = [
'default_language[langcode]' => LanguageInterface::LANGCODE_SITE_DEFAULT,
'default_language[language_alterable]' => TRUE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id());
$this->submitForm($edit, 'Save');
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', 'cc')->isSelected());
}
/**
* Tests that translated terms are displayed correctly on the term overview.
*/
public function testTermTranslatedOnOverviewPage(): void {
// Configure the vocabulary to not hide the language selector.
$edit = [
'default_language[language_alterable]' => TRUE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id());
$this->submitForm($edit, 'Save');
// Add a term.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
// Submit the term.
$edit = [
'name[0][value]' => $this->randomMachineName(),
'langcode[0][value]' => 'aa',
];
$this->submitForm($edit, 'Save');
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => $edit['name[0][value]'],
]);
$term = reset($terms);
// Add a translation for that term.
$translated_title = $this->randomMachineName();
$term->addTranslation('bb', [
'name' => $translated_title,
]);
$term->save();
// Overview page in the other language shows the translated term
$this->drupalGet('bb/admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->responseMatches('|<a[^>]*>' . $translated_title . '</a>|');
}
}

View File

@@ -0,0 +1,363 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\TermInterface;
use Drupal\Tests\BrowserTestBase;
/**
* Tests managing taxonomy parents through the user interface.
*
* @group taxonomy
*/
class TermParentsTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The term storage.
*
* @var \Drupal\taxonomy\TermStorageInterface
*/
protected $termStorage;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The ID of the vocabulary used in this test.
*
* @var string
*/
protected $vocabularyId = 'test_vocabulary';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = $this->container->get('entity_type.manager');
$this->termStorage = $entity_type_manager->getStorage('taxonomy_term');
$this->state = $this->container->get('state');
Vocabulary::create(['vid' => $this->vocabularyId, 'name' => 'Test'])->save();
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
}
/**
* Tests specifying parents when creating terms.
*/
public function testAddWithParents(): void {
$this->drupalGet("/admin/structure/taxonomy/manage/{$this->vocabularyId}/add");
$page = $this->getSession()->getPage();
// Create a term without any parents.
$term_1 = $this->submitAddTermForm('Test term 1');
$expected = [['target_id' => 0]];
$this->assertEquals($expected, $term_1->get('parent')->getValue());
// Explicitly selecting <root> should have the same effect as not selecting
// anything.
$page->selectFieldOption('Parent terms', '<root>');
$term_2 = $this->submitAddTermForm('Test term 2');
$this->assertEquals($expected, $term_2->get('parent')->getValue());
// Create two terms with the previously created ones as parents,
// respectively.
$page->selectFieldOption('Parent terms', 'Test term 1');
$term_3 = $this->submitAddTermForm('Test term 3');
$expected = [['target_id' => $term_1->id()]];
$this->assertEquals($expected, $term_3->get('parent')->getValue());
$page->selectFieldOption('Parent terms', 'Test term 2');
$term_4 = $this->submitAddTermForm('Test term 4');
$expected = [['target_id' => $term_2->id()]];
$this->assertEquals($expected, $term_4->get('parent')->getValue());
// Create a term with term 3 as parent.
$page->selectFieldOption('Parent terms', '-Test term 3');
$term_5 = $this->submitAddTermForm('Test term 5');
$expected = [['target_id' => $term_3->id()]];
$this->assertEquals($expected, $term_5->get('parent')->getValue());
// Create a term with multiple parents.
$page->selectFieldOption('Parent terms', '--Test term 5');
$page->selectFieldOption('Parent terms', '-Test term 4', TRUE);
$term_6 = $this->submitAddTermForm('Test term 6');
$expected = [
['target_id' => $term_5->id()],
['target_id' => $term_4->id()],
];
$this->assertEquals($expected, $term_6->get('parent')->getValue());
}
/**
* Creates a term through the user interface and returns it.
*
* @param string $name
* The name of the term to create.
*
* @return \Drupal\taxonomy\TermInterface
* The newly created taxonomy term.
*/
protected function submitAddTermForm($name) {
$this->getSession()->getPage()->fillField('Name', $name);
$this->submitForm([], 'Save');
$result = $this->termStorage
->getQuery()
->accessCheck(FALSE)
->condition('name', $name)
->execute();
/** @var \Drupal\taxonomy\TermInterface $term_1 */
$term_1 = $this->termStorage->load(reset($result));
$this->assertInstanceOf(TermInterface::class, $term_1);
return $term_1;
}
/**
* Tests editing the parents of existing terms.
*/
public function testEditingParents(): void {
$terms = $this->doTestEditingSingleParent();
$term_5 = array_pop($terms);
$term_4 = array_pop($terms);
// Create a term with multiple parents.
$term_6 = $this->createTerm('Test term 6', [
// Term 5 comes before term 4 in the user interface, so add the parents in
// the matching order.
$term_5->id(),
$term_4->id(),
]);
$this->drupalGet($term_6->toUrl('edit-form'));
$this->assertParentOption('<root>');
$this->assertParentOption('Test term 1');
$this->assertParentOption('-Test term 3');
$this->assertParentOption('--Test term 5', TRUE);
$this->assertParentOption('Test term 2');
$this->assertParentOption('-Test term 4', TRUE);
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_6);
}
/**
* Tests specifying parents when creating terms and a disabled parent form.
*/
public function testEditingParentsWithDisabledFormElement(): void {
// Disable the parent form element.
$this->state->set('taxonomy_test.disable_parent_form_element', TRUE);
$this->drupalGet("/admin/structure/taxonomy/manage/{$this->vocabularyId}/add");
$this->assertSession()->fieldDisabled('Parent terms');
$terms = $this->doTestEditingSingleParent();
$term_5 = array_pop($terms);
$term_4 = array_pop($terms);
// Create a term with multiple parents.
$term_6 = $this->createTerm('Test term 6', [
// When the parent form element is disabled, its default value is used as
// the value which gets populated in ascending order of term IDs.
$term_4->id(),
$term_5->id(),
]);
$this->drupalGet($term_6->toUrl('edit-form'));
$this->assertParentOption('<root>');
$this->assertParentOption('Test term 1');
$this->assertParentOption('-Test term 3');
$this->assertParentOption('--Test term 5', TRUE);
$this->assertParentOption('Test term 2');
$this->assertParentOption('-Test term 4', TRUE);
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_6);
}
/**
* Performs tests that edit terms with a single parent.
*
* @return \Drupal\taxonomy\TermInterface[]
* A list of terms created for testing.
*/
protected function doTestEditingSingleParent() {
$terms = [];
// Create two terms without any parents.
$term_1 = $this->createTerm('Test term 1');
$this->drupalGet($term_1->toUrl('edit-form'));
$this->assertParentOption('<root>', TRUE);
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_1);
$terms[] = $term_1;
$term_2 = $this->createTerm('Test term 2');
$this->drupalGet($term_2->toUrl('edit-form'));
$this->assertParentOption('<root>', TRUE);
$this->assertParentOption('Test term 1');
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_2);
$terms[] = $term_2;
// Create two terms with the previously created terms as parents,
// respectively.
$term_3 = $this->createTerm('Test term 3', [$term_1->id()]);
$this->drupalGet($term_3->toUrl('edit-form'));
$this->assertParentOption('<root>');
$this->assertParentOption('Test term 1', TRUE);
$this->assertParentOption('Test term 2');
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_3);
$terms[] = $term_3;
$term_4 = $this->createTerm('Test term 4', [$term_2->id()]);
$this->drupalGet($term_4->toUrl('edit-form'));
$this->assertParentOption('<root>');
$this->assertParentOption('Test term 1');
$this->assertParentOption('-Test term 3');
$this->assertParentOption('Test term 2', TRUE);
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_4);
$terms[] = $term_4;
// Create a term with term 3 as parent.
$term_5 = $this->createTerm('Test term 5', [$term_3->id()]);
$this->drupalGet($term_5->toUrl('edit-form'));
$this->assertParentOption('<root>');
$this->assertParentOption('Test term 1');
$this->assertParentOption('-Test term 3', TRUE);
$this->assertParentOption('Test term 2');
$this->assertParentOption('-Test term 4');
$this->submitForm([], 'Save');
$this->assertParentsUnchanged($term_5);
$terms[] = $term_5;
return $terms;
}
/**
* Test the term add/edit form with parent query parameter.
*/
public function testParentFromQuery(): void {
// Create three terms without any parents.
$term_1 = $this->createTerm('Test term 1');
$term_2 = $this->createTerm('Test term 2');
$term_3 = $this->createTerm('Test term 3');
// Add term form with one parent.
$this->drupalGet("/admin/structure/taxonomy/manage/{$this->vocabularyId}/add", ['query' => ['parent' => $term_1->id()]]);
$this->assertParentOption('Test term 1', TRUE);
$this->assertParentOption('Test term 2', FALSE);
$this->assertParentOption('Test term 3', FALSE);
// Add term form with two parents.
$this->drupalGet("/admin/structure/taxonomy/manage/{$this->vocabularyId}/add", ['query' => ['parent[0]' => $term_1->id(), 'parent[1]' => $term_2->id()]]);
$this->assertParentOption('Test term 1', TRUE);
$this->assertParentOption('Test term 2', TRUE);
$this->assertParentOption('Test term 3', FALSE);
// Add term form with no parents.
$this->drupalGet("/admin/structure/taxonomy/manage/{$this->vocabularyId}/add", ['query' => ['parent' => '']]);
$this->assertParentOption('Test term 1', FALSE);
$this->assertParentOption('Test term 2', FALSE);
$this->assertParentOption('Test term 3', FALSE);
// Add term form with invalid parent.
$this->drupalGet("/admin/structure/taxonomy/manage/{$this->vocabularyId}/add", ['query' => ['parent' => -1]]);
$this->assertParentOption('Test term 1', FALSE);
$this->assertParentOption('Test term 2', FALSE);
$this->assertParentOption('Test term 3', FALSE);
// Edit term form with one parent.
$this->drupalGet($term_1->toUrl('edit-form'), ['query' => ['parent' => $term_2->id()]]);
$this->assertParentOption('Test term 2', TRUE);
$this->assertParentOption('Test term 3', FALSE);
// Edit term form with two parents.
$this->drupalGet($term_1->toUrl('edit-form'), ['query' => ['parent[0]' => $term_2->id(), 'parent[1]' => $term_3->id()]]);
$this->assertParentOption('Test term 2', TRUE);
$this->assertParentOption('Test term 3', TRUE);
// Edit term form with no parents.
$this->drupalGet($term_1->toUrl('edit-form'), ['query' => ['parent' => '']]);
$this->assertParentOption('Test term 2', FALSE);
$this->assertParentOption('Test term 3', FALSE);
// Edit term form with invalid parent.
$this->drupalGet($term_1->toUrl('edit-form'), ['query' => ['parent' => -1]]);
$this->assertParentOption('Test term 2', FALSE);
$this->assertParentOption('Test term 3', FALSE);
}
/**
* Creates a term, saves it and returns it.
*
* @param string $name
* The name of the term to create
* @param int[] $parent_ids
* (optional) A list of parent term IDs.
*
* @return \Drupal\taxonomy\TermInterface
* The created term.
*/
protected function createTerm($name, array $parent_ids = []) {
/** @var \Drupal\taxonomy\TermInterface $term */
$term = $this->termStorage->create([
'name' => $name,
'vid' => $this->vocabularyId,
]);
foreach ($parent_ids as $delta => $parent_id) {
$term->get('parent')->set($delta, ['target_id' => $parent_id]);
}
$term->save();
return $term;
}
/**
* Asserts that an option in the parent form element of terms exists.
*
* @param string $option
* The label of the parent option.
* @param bool $selected
* (optional) Whether or not the option should be selected. Defaults to
* FALSE.
*
* @internal
*/
protected function assertParentOption(string $option, bool $selected = FALSE): void {
$option = $this->assertSession()->optionExists('Parent terms', $option);
if ($selected) {
$this->assertTrue($option->hasAttribute('selected'));
}
else {
$this->assertFalse($option->hasAttribute('selected'));
}
}
/**
* Asserts that the parents of the term have not changed after saving.
*
* @param \Drupal\taxonomy\TermInterface $term
* The term to check.
*
* @internal
*/
protected function assertParentsUnchanged(TermInterface $term): void {
$saved_term = $this->termStorage->load($term->id());
$expected = $term->get('parent')->getValue();
$this->assertEquals($expected, $saved_term->get('parent')->getValue());
}
}

View File

@@ -0,0 +1,685 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\TermInterface;
use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait;
/**
* Tests load, save and delete for taxonomy terms.
*
* @group taxonomy
* @group #slow
*/
class TermTest extends TaxonomyTestBase {
use AssertBreadcrumbTrait;
/**
* Vocabulary for testing.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* Taxonomy term reference field for testing.
*
* @var \Drupal\field\FieldConfigInterface
*/
protected $field;
/**
* Modules to enable.
*
* @var string[]
*/
protected static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('page_title_block');
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'access taxonomy overview',
'bypass node access',
]));
$this->vocabulary = $this->createVocabulary();
$field_name = 'taxonomy_' . $this->vocabulary->id();
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $field_name, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$this->field = FieldConfig::loadByName('node', 'article', $field_name);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($field_name, [
'type' => 'options_select',
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($field_name, [
'type' => 'entity_reference_label',
])
->save();
}
/**
* The "parent" field must restrict references to the same vocabulary.
*/
public function testParentHandlerSettings(): void {
$vocabulary_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('taxonomy_term', $this->vocabulary->id());
$parent_target_bundles = $vocabulary_fields['parent']->getSetting('handler_settings')['target_bundles'];
$this->assertSame([$this->vocabulary->id() => $this->vocabulary->id()], $parent_target_bundles);
}
/**
* Tests terms in a single and multiple hierarchy.
*/
public function testTaxonomyTermHierarchy(): void {
// Create two taxonomy terms.
$term1 = $this->createTerm($this->vocabulary);
$term2 = $this->createTerm($this->vocabulary);
// Get the taxonomy storage.
/** @var \Drupal\taxonomy\TermStorageInterface $taxonomy_storage */
$taxonomy_storage = $this->container->get('entity_type.manager')->getStorage('taxonomy_term');
// Check that hierarchy is flat.
$this->assertEquals(0, $taxonomy_storage->getVocabularyHierarchyType($this->vocabulary->id()), 'Vocabulary is flat.');
// Edit $term2, setting $term1 as parent.
$edit = [];
$edit['parent[]'] = [$term1->id()];
$this->drupalGet('taxonomy/term/' . $term2->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check the hierarchy.
$children = $taxonomy_storage->loadChildren($term1->id());
$parents = $taxonomy_storage->loadParents($term2->id());
$this->assertTrue(isset($children[$term2->id()]), 'Child found correctly.');
$this->assertTrue(isset($parents[$term1->id()]), 'Parent found correctly.');
// Load and save a term, confirming that parents are still set.
$term = Term::load($term2->id());
$term->save();
$parents = $taxonomy_storage->loadParents($term2->id());
$this->assertTrue(isset($parents[$term1->id()]), 'Parent found correctly.');
// Create a third term and save this as a parent of term2.
$term3 = $this->createTerm($this->vocabulary);
$term2->parent = [$term1->id(), $term3->id()];
$term2->save();
$parents = $taxonomy_storage->loadParents($term2->id());
$this->assertArrayHasKey($term1->id(), $parents);
$this->assertArrayHasKey($term3->id(), $parents);
}
/**
* Tests that many terms with parents show on each page.
*/
public function testTaxonomyTermChildTerms(): void {
// Set limit to 10 terms per page. Set variable to 9 so 10 terms appear.
$this->config('taxonomy.settings')->set('terms_per_page_admin', '9')->save();
$term1 = $this->createTerm($this->vocabulary);
$terms_array = [];
$taxonomy_storage = $this->container->get('entity_type.manager')->getStorage('taxonomy_term');
// Create 40 terms. Terms 1-12 get parent of $term1. All others are
// individual terms.
for ($x = 1; $x <= 40; $x++) {
$edit = [];
// Set terms in order so we know which terms will be on which pages.
$edit['weight'] = $x;
// Set terms 1-20 to be children of first term created.
if ($x <= 12) {
$edit['parent'] = $term1->id();
}
$term = $this->createTerm($this->vocabulary, $edit);
$children = $taxonomy_storage->loadChildren($term1->id());
$parents = $taxonomy_storage->loadParents($term->id());
$terms_array[$x] = Term::load($term->id());
}
// Get Page 1. Parent term and terms 1-13 are displayed.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->pageTextContains($term1->getName());
for ($x = 1; $x <= 13; $x++) {
$this->assertSession()->pageTextContains($terms_array[$x]->getName());
}
// Get Page 2. Parent term and terms 1-18 are displayed.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', ['query' => ['page' => 1]]);
$this->assertSession()->pageTextContains($term1->getName());
for ($x = 1; $x <= 18; $x++) {
$this->assertSession()->pageTextContains($terms_array[$x]->getName());
}
// Get Page 3. No parent term and no terms <18 are displayed. Terms 18-25
// are displayed.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', ['query' => ['page' => 2]]);
$this->assertSession()->pageTextNotContains($term1->getName());
for ($x = 1; $x <= 17; $x++) {
$this->assertSession()->pageTextNotContains($terms_array[$x]->getName());
}
for ($x = 18; $x <= 25; $x++) {
$this->assertSession()->pageTextContains($terms_array[$x]->getName());
}
}
/**
* Tests term creation with a free-tagging vocabulary from the node form.
*/
public function testNodeTermCreationAndDeletion(): void {
// Enable tags in the vocabulary.
$field = $this->field;
\Drupal::service('entity_display.repository')
->getFormDisplay($field->getTargetEntityTypeId(), $field->getTargetBundle())
->setComponent($field->getName(), [
'type' => 'entity_reference_autocomplete_tags',
'settings' => [
'placeholder' => 'Start typing here.',
],
])
->save();
// Prefix the terms with a letter to ensure there is no clash in the first
// three letters.
// @see https://www.drupal.org/node/2397691
$terms = [
'term1' => 'a' . $this->randomMachineName(),
'term2' => 'b' . $this->randomMachineName(),
'term3' => 'c' . $this->randomMachineName() . ', ' . $this->randomMachineName(),
'term4' => 'd' . $this->randomMachineName(),
];
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['body[0][value]'] = $this->randomMachineName();
// Insert the terms in a comma separated list. Vocabulary 1 is a
// free-tagging field created by the default profile.
$edit[$field->getName() . '[target_id]'] = Tags::implode($terms);
// Verify the placeholder is there.
$this->drupalGet('node/add/article');
$this->assertSession()->responseContains('placeholder="Start typing here."');
// Preview and verify the terms appear but are not created.
$this->submitForm($edit, 'Preview');
foreach ($terms as $term) {
$this->assertSession()->pageTextContains($term);
}
$tree = $this->container->get('entity_type.manager')->getStorage('taxonomy_term')->loadTree($this->vocabulary->id());
$this->assertEmpty($tree, 'The terms are not created on preview.');
// Save, creating the terms.
$this->drupalGet('node/add/article');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Article ' . $edit['title[0][value]'] . ' has been created.');
// Verify that the creation message contains a link to a node.
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "node/")]');
foreach ($terms as $term) {
$this->assertSession()->pageTextContains($term);
}
// Get the created terms.
$term_objects = [];
foreach ($terms as $key => $term) {
$term_objects[$key] = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => $term,
]);
$term_objects[$key] = reset($term_objects[$key]);
}
// Get the node.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
// Test editing the node.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
foreach ($terms as $term) {
$this->assertSession()->pageTextContains($term);
}
// Delete term 1 from the term edit page.
$this->drupalGet('taxonomy/term/' . $term_objects['term1']->id() . '/edit');
$this->clickLink('Delete');
$this->submitForm([], 'Delete');
// Delete term 2 from the term delete page.
$this->drupalGet('taxonomy/term/' . $term_objects['term2']->id() . '/delete');
$this->submitForm([], 'Delete');
// Verify that the terms appear on the node page after the two terms were
// deleted.
$term_names = [$term_objects['term3']->getName(), $term_objects['term4']->getName()];
$this->drupalGet('node/' . $node->id());
foreach ($term_names as $term_name) {
$this->assertSession()->pageTextContains($term_name);
}
$this->assertSession()->pageTextNotContains($term_objects['term1']->getName());
$this->assertSession()->pageTextNotContains($term_objects['term2']->getName());
}
/**
* Save, edit and delete a term using the user interface.
*/
public function testTermInterface(): void {
\Drupal::service('module_installer')->install(['views']);
$edit = [
'name[0][value]' => $this->randomMachineName(12),
'description[0][value]' => $this->randomMachineName(100),
];
// Explicitly set the parents field to 'root', to ensure that
// TermForm::save() handles the invalid term ID correctly.
$edit['parent[]'] = [0];
// Create the term to edit.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->submitForm($edit, 'Save');
// Ensure form redirected back to term add page.
$this->assertSession()->addressEquals('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => $edit['name[0][value]'],
]);
$term = reset($terms);
$this->assertNotNull($term, 'Term found in database.');
// Submitting a term takes us to the add page; we need the List page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->clickLink('Edit', 1);
// Verify that the randomly generated term is present.
$this->assertSession()->pageTextContains($edit['name[0][value]']);
$this->assertSession()->pageTextContains($edit['description[0][value]']);
// Test the "Add child" link on the overview page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->linkExistsExact('Add child');
$this->clickLink('Add child');
$edit = [
'name[0][value]' => 'Child term',
];
$this->submitForm($edit, 'Save');
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => 'Child term',
]);
$child = reset($terms);
$this->assertNotNull($child, 'Child term found in database.');
$this->assertEquals($term->id(), $child->get('parent')->getValue()[0]['target_id']);
// Edit the term.
$edit = [
'name[0][value]' => $this->randomMachineName(14),
'description[0][value]' => $this->randomMachineName(102),
];
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->submitForm($edit, 'Save');
// Ensure form redirected back to term view.
$this->assertSession()->addressEquals('taxonomy/term/' . $term->id());
// Check that the term is still present at admin UI after edit.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->pageTextContains($edit['name[0][value]']);
$this->assertSession()->linkExists('Edit');
// Unpublish the term.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->submitForm(["status[value]" => 0], 'Save');
// Check that the term is now unpublished in the list.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->elementTextContains('css', "#edit-terms-tid{$term->id()}0-status", 'Unpublished');
// Check the term link can be clicked through to the term page.
$this->clickLink($edit['name[0][value]']);
$this->assertSession()->statusCodeEquals(200);
// View the term and check that it is correct.
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->pageTextContains($edit['name[0][value]']);
$this->assertSession()->pageTextContains($edit['description[0][value]']);
// Did this page request display a 'term-listing-heading'?
$this->assertSession()->elementExists('xpath', '//div[@class="views-element-container"]/div/header/div/div/p');
// Check that it does NOT show a description when description is blank.
$term->setDescription(NULL);
$term->save();
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->elementNotExists('xpath', '//div[@class="views-element-container"]/div/header/div/div/p');
// Check that the description value is processed.
$value = $this->randomMachineName();
$term->setDescription($value);
$term->save();
$this->assertSame("<p>{$value}</p>\n", (string) $term->description->processed);
// Check that the term feed page is working.
$this->drupalGet('taxonomy/term/' . $term->id() . '/feed');
// Delete the term.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->clickLink('Delete');
$this->submitForm([], 'Delete');
// Assert that the term no longer exists.
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->statusCodeEquals(404);
// Test "save and go to list" action while creating term.
// Create the term to edit.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$edit = [
'name[0][value]' => $this->randomMachineName(12),
'description[0][value]' => $this->randomMachineName(100),
];
// Create the term to edit.
$this->submitForm($edit, 'Save and go to list');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->addressEquals('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->pageTextContains($edit['name[0][value]']);
// Validate that "Save and go to list" doesn't exist when destination
// parameter is present.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add', ['query' => ['destination' => 'node/add']]);
$this->assertSession()->pageTextNotContains('Save and go to list');
// Validate that "Save and go to list" doesn't exist when missing permission
// 'access taxonomy overview'.
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'bypass node access',
]));
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->assertSession()->pageTextNotContains('Save and go to list');
}
/**
* Test UI with override_selector TRUE.
*/
public function testTermSaveOverrideSelector(): void {
$this->config('taxonomy.settings')->set('override_selector', TRUE)->save();
// Create a Term.
$edit = [
'name[0][value]' => $this->randomMachineName(12),
'description[0][value]' => $this->randomMachineName(100),
];
// Create the term to edit.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->submitForm($edit, 'Save');
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => $edit['name[0][value]'],
]);
$term = reset($terms);
$this->assertNotNull($term, 'Term found in database.');
// The term appears on the vocab list page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->pageTextContains($term->getName());
}
/**
* Save, edit and delete a term using the user interface.
*/
public function testTermReorder(): void {
$assert = $this->assertSession();
$this->createTerm($this->vocabulary);
$this->createTerm($this->vocabulary);
$this->createTerm($this->vocabulary);
$taxonomy_storage = $this->container->get('entity_type.manager')->getStorage('taxonomy_term');
// Fetch the created terms in the default alphabetical order, i.e. term1
// precedes term2 alphabetically, and term2 precedes term3.
$taxonomy_storage->resetCache();
[$term1, $term2, $term3] = $taxonomy_storage->loadTree($this->vocabulary->id(), 0, NULL, TRUE);
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
// Each term has four hidden fields, "tid:1:0[tid]", "tid:1:0[parent]",
// "tid:1:0[depth]", and "tid:1:0[weight]". Change the order to term2,
// term3, term1 by setting weight property, make term3 a child of term2 by
// setting the parent and depth properties, and update all hidden fields.
$hidden_edit = [
'terms[tid:' . $term2->id() . ':0][term][tid]' => $term2->id(),
'terms[tid:' . $term2->id() . ':0][term][parent]' => 0,
'terms[tid:' . $term2->id() . ':0][term][depth]' => 0,
'terms[tid:' . $term3->id() . ':0][term][tid]' => $term3->id(),
'terms[tid:' . $term3->id() . ':0][term][parent]' => $term2->id(),
'terms[tid:' . $term3->id() . ':0][term][depth]' => 1,
'terms[tid:' . $term1->id() . ':0][term][tid]' => $term1->id(),
'terms[tid:' . $term1->id() . ':0][term][parent]' => 0,
'terms[tid:' . $term1->id() . ':0][term][depth]' => 0,
];
// Because we can't post hidden form elements, we have to change them in
// code here, and then submit.
foreach ($hidden_edit as $field => $value) {
$node = $assert->hiddenFieldExists($field);
$node->setValue($value);
}
// Edit non-hidden elements within submitForm().
$edit = [
'terms[tid:' . $term2->id() . ':0][weight]' => 0,
'terms[tid:' . $term3->id() . ':0][weight]' => 1,
'terms[tid:' . $term1->id() . ':0][weight]' => 2,
];
$this->submitForm($edit, 'Save');
$taxonomy_storage->resetCache();
$terms = $taxonomy_storage->loadTree($this->vocabulary->id());
$this->assertEquals($term2->id(), $terms[0]->tid, 'Term 2 was moved above term 1.');
$this->assertEquals([$term2->id()], $terms[1]->parents, 'Term 3 was made a child of term 2.');
$this->assertEquals($term1->id(), $terms[2]->tid, 'Term 1 was moved below term 2.');
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->submitForm([], 'Reset to alphabetical');
// Submit confirmation form.
$this->submitForm([], 'Reset to alphabetical');
// Ensure form redirected back to overview.
$this->assertSession()->addressEquals('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$taxonomy_storage->resetCache();
$terms = $taxonomy_storage->loadTree($this->vocabulary->id(), 0, NULL, TRUE);
$this->assertEquals($term1->id(), $terms[0]->id(), 'Term 1 was moved to back above term 2.');
$this->assertEquals($term2->id(), $terms[1]->id(), 'Term 2 was moved to back below term 1.');
$this->assertEquals($term3->id(), $terms[2]->id(), 'Term 3 is still below term 2.');
$this->assertEquals([$term2->id()], $terms[2]->parents, 'Term 3 is still a child of term 2.');
}
/**
* Tests saving a term with multiple parents through the UI.
*/
public function testTermMultipleParentsInterface(): void {
// Add two new terms to the vocabulary so that we can have multiple parents.
// These will be terms with tids of 1 and 2 respectively.
$this->createTerm($this->vocabulary);
$this->createTerm($this->vocabulary);
// Add a new term with multiple parents.
$edit = [
'name[0][value]' => $this->randomMachineName(12),
'description[0][value]' => $this->randomMachineName(100),
'parent[]' => [0, 1],
];
// Save the new term.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->submitForm($edit, 'Save');
// Check that the term was successfully created.
$term = $this->reloadTermByName($edit['name[0][value]']);
$this->assertNotNull($term, 'Term found in database.');
$this->assertEquals($edit['name[0][value]'], $term->getName(), 'Term name was successfully saved.');
$this->assertEquals($edit['description[0][value]'], $term->getDescription(), 'Term description was successfully saved.');
// Check that we have the expected parents.
$this->assertEquals([0, 1], $this->getParentTids($term), 'Term parents (root plus one) were successfully saved.');
// Load the edit form and save again to ensure parent are preserved.
// Generate a new name, so we know that the term really is saved.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$edit = [
'name[0][value]' => $this->randomMachineName(12),
];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Updated term ' . $edit['name[0][value]']);
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains('Updated term ' . $edit['name[0][value]']);
// Check that we still have the expected parents.
$term = $this->reloadTermByName($edit['name[0][value]']);
$this->assertEquals([0, 1], $this->getParentTids($term), 'Term parents (root plus one) were successfully saved again.');
// Save with two real parents. i.e., not including <root>.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$edit = [
'name[0][value]' => $this->randomMachineName(12),
'parent[]' => [1, 2],
];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Updated term ' . $edit['name[0][value]']);
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains('Updated term ' . $edit['name[0][value]']);
// Check that we have the expected parents.
$term = $this->reloadTermByName($edit['name[0][value]']);
$this->assertEquals([1, 2], $this->getParentTids($term), 'Term parents (two real) were successfully saved.');
}
/**
* Reloads a term by name.
*
* @param string $name
* The name of the term.
*
* @return \Drupal\taxonomy\TermInterface
* The reloaded term.
*/
private function reloadTermByName(string $name): TermInterface {
\Drupal::entityTypeManager()->getStorage('taxonomy_term')->resetCache();
/** @var \Drupal\taxonomy\TermInterface[] $terms */
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['name' => $name]);
return reset($terms);
}
/**
* Get the parent tids for a term including root.
*
* @param \Drupal\taxonomy\TermInterface $term
* The term.
*
* @return array
* A sorted array of tids and 0 if the root is a parent.
*/
private function getParentTids($term) {
$parent_tids = [];
foreach ($term->get('parent') as $item) {
$parent_tids[] = (int) $item->target_id;
}
sort($parent_tids);
return $parent_tids;
}
/**
* Tests that editing and saving a node with no changes works correctly.
*/
public function testReSavingTags(): void {
// Enable tags in the vocabulary.
$field = $this->field;
\Drupal::service('entity_display.repository')
->getFormDisplay($field->getTargetEntityTypeId(), $field->getTargetBundle())
->setComponent($field->getName(), [
'type' => 'entity_reference_autocomplete_tags',
])
->save();
// Create a term and a node using it.
$term = $this->createTerm($this->vocabulary);
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit['body[0][value]'] = $this->randomMachineName(16);
$edit[$this->field->getName() . '[target_id]'] = $term->getName();
$this->drupalGet('node/add/article');
$this->submitForm($edit, 'Save');
// Check that the term is displayed when editing and saving the node with no
// changes.
$this->clickLink('Edit');
$this->assertSession()->responseContains($term->getName());
$this->submitForm([], 'Save');
$this->assertSession()->responseContains($term->getName());
}
/**
* Check the breadcrumb on edit and delete a term page.
*/
public function testTermBreadcrumbs(): void {
$edit = [
'name[0][value]' => $this->randomMachineName(14),
'description[0][value]' => $this->randomMachineName(100),
'parent[]' => [0],
];
// Create the term.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
$this->submitForm($edit, 'Save');
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'name' => $edit['name[0][value]'],
]);
$term = reset($terms);
$this->assertNotNull($term, 'Term found in database.');
// Check the breadcrumb on the term edit page.
$trail = [
'' => 'Home',
'taxonomy/term/' . $term->id() => $term->label(),
];
$this->assertBreadcrumb('taxonomy/term/' . $term->id() . '/edit', $trail);
$this->assertSession()->assertEscaped($term->label());
// Check the breadcrumb on the term delete page.
$trail = [
'' => 'Home',
'taxonomy/term/' . $term->id() => $term->label(),
];
$this->assertBreadcrumb('taxonomy/term/' . $term->id() . '/delete', $trail);
$this->assertSession()->assertEscaped($term->label());
}
}

View File

@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\node\Entity\Node;
/**
* Tests the translation of taxonomy terms field on nodes.
*
* @group taxonomy
*/
class TermTranslationFieldViewTest extends TaxonomyTestBase {
use TaxonomyTranslationTestTrait;
/**
* The term that should be translated.
*
* @var \Drupal\taxonomy\Entity\Term
*/
protected $term;
/**
* The tag in the source language.
*
* @var string
*/
protected $baseTagName = 'OriginalTagName';
/**
* The translated value for the tag.
*
* @var string
*/
protected $translatedTagName = 'TranslatedTagName';
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['language', 'content_translation', 'taxonomy'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->setupLanguages();
$this->vocabulary = $this->createVocabulary();
$this->enableTranslation();
$this->setUpTerm();
$this->setUpTermReferenceField();
$this->setUpNode();
}
/**
* Tests if the translated taxonomy term is displayed.
*/
public function testTranslatedTaxonomyTermReferenceDisplay(): void {
$path = 'node/' . $this->node->id();
$translation_path = $this->translateToLangcode . '/' . $path;
$this->drupalGet($path);
$this->assertSession()->pageTextNotContains($this->translatedTagName);
$this->assertSession()->pageTextContains($this->baseTagName);
$this->drupalGet($translation_path);
$this->assertSession()->pageTextContains($this->translatedTagName);
$this->assertSession()->pageTextNotContains($this->baseTagName);
}
/**
* Creates a test subject node, with translation.
*/
protected function setUpNode() {
/** @var \Drupal\node\Entity\Node $node */
$node = Node::create([
'title' => $this->randomMachineName(),
'type' => 'article',
'description' => [
[
'value' => $this->randomMachineName(),
'format' => 'basic_html',
],
],
$this->termFieldName => [['target_id' => $this->term->id()]],
'langcode' => $this->baseLangcode,
]);
$node->save();
$node->addTranslation($this->translateToLangcode, $node->toArray());
$node->save();
$this->node = $node;
}
/**
* Creates a test subject term, with translation.
*/
protected function setUpTerm() {
$this->term = $this->createTerm($this->vocabulary, [
'name' => $this->baseTagName,
'langcode' => $this->baseLangcode,
]);
$this->term->addTranslation($this->translateToLangcode, [
'name' => $this->translatedTagName,
]);
$this->term->save();
}
}

View File

@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Core\Url;
use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait;
/**
* Tests for proper breadcrumb translation.
*
* @group taxonomy
*/
class TermTranslationTest extends TaxonomyTestBase {
use AssertBreadcrumbTrait;
use TaxonomyTranslationTestTrait;
/**
* Term to translated term mapping.
*
* @var array
*/
protected $termTranslationMap = [
'one' => 'translatedOne',
'two' => 'translatedTwo',
'three' => 'translatedThree',
];
/**
* Created terms.
*
* @var \Drupal\taxonomy\Entity\Term[]
*/
protected $terms = [];
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'language', 'content_translation'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->setupLanguages();
$this->vocabulary = $this->createVocabulary();
$this->enableTranslation();
$this->setUpTerms();
$this->setUpTermReferenceField();
}
/**
* Tests translated breadcrumbs.
*/
public function testTranslatedBreadcrumbs(): void {
// Ensure non-translated breadcrumb is correct.
$breadcrumb = [Url::fromRoute('<front>')->toString() => 'Home'];
foreach ($this->terms as $term) {
$breadcrumb[$term->toUrl()->toString()] = $term->label();
}
// The last item will not be in the breadcrumb.
array_pop($breadcrumb);
// Check the breadcrumb on the leaf term page.
$term = $this->getLeafTerm();
$this->assertBreadcrumb($term->toUrl(), $breadcrumb, $term->label());
$languages = \Drupal::languageManager()->getLanguages();
// Construct the expected translated breadcrumb.
$breadcrumb = [Url::fromRoute('<front>', [], ['language' => $languages[$this->translateToLangcode]])->toString() => 'Home'];
foreach ($this->terms as $term) {
$translated = $term->getTranslation($this->translateToLangcode);
$url = $translated->toUrl('canonical', ['language' => $languages[$this->translateToLangcode]])->toString();
$breadcrumb[$url] = $translated->label();
}
array_pop($breadcrumb);
// Check for the translated breadcrumb on the translated leaf term page.
$term = $this->getLeafTerm();
$translated = $term->getTranslation($this->translateToLangcode);
$this->assertBreadcrumb($translated->toUrl('canonical', ['language' => $languages[$this->translateToLangcode]]), $breadcrumb, $translated->label());
}
/**
* Tests translation of terms are showed in the node.
*/
public function testTermsTranslation(): void {
// Set the display of the term reference field on the article content type
// to "Check boxes/radio buttons".
\Drupal::service('entity_display.repository')
->getFormDisplay('node', 'article')
->setComponent($this->termFieldName, [
'type' => 'options_buttons',
])
->save();
$this->drupalLogin($this->drupalCreateUser(['create article content']));
// Test terms are listed.
$this->drupalGet('node/add/article');
$this->assertSession()->pageTextContains('one');
$this->assertSession()->pageTextContains('two');
$this->assertSession()->pageTextContains('three');
// Test terms translated are listed.
$this->drupalGet('hu/node/add/article');
$this->assertSession()->pageTextContains('translatedOne');
$this->assertSession()->pageTextContains('translatedTwo');
$this->assertSession()->pageTextContains('translatedThree');
}
/**
* Setup translated terms in a hierarchy.
*/
protected function setUpTerms() {
$parent_vid = 0;
foreach ($this->termTranslationMap as $name => $translation) {
$term = $this->createTerm($this->vocabulary, [
'name' => $name,
'langcode' => $this->baseLangcode,
'parent' => $parent_vid,
]);
$term->addTranslation($this->translateToLangcode, [
'name' => $translation,
]);
$term->save();
// Each term is nested under the last.
$parent_vid = $term->id();
$this->terms[] = $term;
}
}
/**
* Get the final (leaf) term in the hierarchy.
*
* @return \Drupal\taxonomy\Entity\Term
* The final term in the hierarchy.
*/
protected function getLeafTerm() {
return $this->terms[count($this->termTranslationMap) - 1];
}
}

View File

@@ -0,0 +1,223 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
use Drupal\Core\Language\LanguageInterface;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the Term Translation UI.
*
* @group taxonomy
*/
class TermTranslationUITest extends ContentTranslationUITestBase {
/**
* The vocabulary used for creating terms.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* {@inheritdoc}
*/
protected $defaultCacheContexts = [
'languages:language_interface',
'theme',
'url.query_args:_wrapper_format',
'user.permissions',
'url.site',
];
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['language', 'content_translation', 'taxonomy'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
$this->entityTypeId = 'taxonomy_term';
$this->bundle = 'tags';
parent::setUp();
$this->doSetup();
}
/**
* {@inheritdoc}
*/
protected function setupBundle() {
parent::setupBundle();
// Create a vocabulary.
$this->vocabulary = Vocabulary::create([
'name' => $this->bundle,
'description' => $this->randomMachineName(),
'vid' => $this->bundle,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'weight' => mt_rand(0, 10),
]);
$this->vocabulary->save();
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), ['administer taxonomy']);
}
/**
* {@inheritdoc}
*/
protected function getNewEntityValues($langcode) {
return ['name' => $this->randomMachineName()] + parent::getNewEntityValues($langcode);
}
/**
* Returns an edit array containing the values to be posted.
*/
protected function getEditValues($values, $langcode, $new = FALSE) {
$edit = parent::getEditValues($values, $langcode, $new);
// To be able to post values for the configurable base fields (name,
// description) have to be suffixed with [0][value].
foreach ($edit as $property => $value) {
foreach (['name', 'description'] as $key) {
if ($property == $key) {
$edit[$key . '[0][value]'] = $value;
unset($edit[$property]);
}
}
}
return $edit;
}
/**
* {@inheritdoc}
*/
public function testTranslationUI(): void {
parent::testTranslationUI();
// Make sure that no row was inserted for taxonomy vocabularies which do
// not have translations enabled.
$tids = \Drupal::entityQueryAggregate('taxonomy_term')
->accessCheck(FALSE)
->aggregate('tid', 'COUNT')
->condition('vid', $this->bundle, '<>')
->groupBy('tid')
->execute();
foreach ($tids as $tid) {
$this->assertTrue($tid['tid_count'] < 2, 'Term does have translations.');
}
}
/**
* Tests translate link on vocabulary term list.
*/
public function testTranslateLinkVocabularyAdminPage(): void {
$this->drupalLogin($this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), ['access administration pages', 'administer taxonomy'])));
$values = [
'name' => $this->randomMachineName(),
];
$translatable_tid = $this->createEntity($values, $this->langcodes[0], $this->vocabulary->id());
// Create an untranslatable vocabulary.
$untranslatable_vocabulary = Vocabulary::create([
'name' => 'untranslatable_voc',
'description' => $this->randomMachineName(),
'vid' => 'untranslatable_voc',
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'weight' => mt_rand(0, 10),
]);
$untranslatable_vocabulary->save();
$values = [
'name' => $this->randomMachineName(),
];
$untranslatable_tid = $this->createEntity($values, $this->langcodes[0], $untranslatable_vocabulary->id());
// Verify translation links.
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists('term/' . $translatable_tid . '/translations', 0, 'The translations link exists for a translatable vocabulary.');
$this->assertSession()->linkByHrefExists('term/' . $translatable_tid . '/edit', 0, 'The edit link exists for a translatable vocabulary.');
$this->drupalGet('admin/structure/taxonomy/manage/' . $untranslatable_vocabulary->id() . '/overview');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists('term/' . $untranslatable_tid . '/edit');
$this->assertSession()->linkByHrefNotExists('term/' . $untranslatable_tid . '/translations');
}
/**
* {@inheritdoc}
*/
protected function doTestTranslationEdit() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {
// We only want to test the title for non-english translations.
if ($langcode != 'en') {
$options = ['language' => $languages[$langcode]];
$url = $entity->toUrl('edit-form', $options);
$this->drupalGet($url);
$this->assertSession()->pageTextContains("{$entity->getTranslation($langcode)->label()} [{$languages[$langcode]->getName()} translation]");
}
}
}
/**
* {@inheritdoc}
*/
protected function doTestPublishedStatus() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
$statuses = [
TRUE,
FALSE,
];
foreach ($statuses as $index => $value) {
// (Un)publish the term translations and check that the translation
// statuses are (un)published accordingly.
foreach ($this->langcodes as $langcode) {
$options = ['language' => $languages[$langcode]];
$url = $entity->toUrl('edit-form', $options);
$this->drupalGet($url, $options);
$this->submitForm(['status[value]' => $value], 'Save');
}
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
foreach ($this->langcodes as $langcode) {
// The term is created as unpublished thus we switch to the published
// status first.
$status = !$index;
$translation = $entity->getTranslation($langcode);
$this->assertEquals($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
}
}
}
}

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
/**
* Verifies that various taxonomy pages use the expected theme.
*
* @group taxonomy
*/
class ThemeTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Make sure we are using distinct default and administrative themes for
// the duration of these tests.
\Drupal::service('theme_installer')->install(['olivero', 'claro']);
$this->config('system.theme')
->set('default', 'olivero')
->set('admin', 'claro')
->save();
// Create and log in as a user who has permission to add and edit taxonomy
// terms and view the administrative theme.
$admin_user = $this->drupalCreateUser([
'administer taxonomy',
'view the administration theme',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests the theme used when adding, viewing and editing taxonomy terms.
*/
public function testTaxonomyTermThemes(): void {
// Adding a term to a vocabulary is considered an administrative action and
// should use the administrative theme.
$vocabulary = $this->createVocabulary();
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
// Check that the administrative theme's CSS appears on the page for adding
// a taxonomy term.
$this->assertSession()->responseContains('claro/css/base/elements.css');
// Viewing a taxonomy term should use the default theme.
$term = $this->createTerm($vocabulary);
$this->drupalGet('taxonomy/term/' . $term->id());
// Check that the default theme's CSS appears on the page for viewing
// a taxonomy term.
$this->assertSession()->responseContains('olivero/css/base/base.css');
// Editing a taxonomy term should use the same theme as adding one.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
// Check that the administrative theme's CSS appears on the page for editing
// a taxonomy term.
$this->assertSession()->responseContains('claro/css/base/elements.css');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the upgrade path for making vocabularies' description NULL.
*
* @group taxonomy
* @see taxonomy_post_update_set_vocabulary_description_to_null()
*/
class NullDescriptionTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.filled.standard.php.gz',
__DIR__ . '/../../../fixtures/update/remove-description-from-tags-vocabulary.php',
];
}
/**
* Tests the upgrade path for updating empty description to NULL.
*/
public function testRunUpdates(): void {
$vocabulary = Vocabulary::load('tags');
$this->assertInstanceOf(Vocabulary::class, $vocabulary);
$this->assertSame("\n", $vocabulary->get('description'));
$this->runUpdates();
$vocabulary = Vocabulary::load('tags');
$this->assertInstanceOf(Vocabulary::class, $vocabulary);
$this->assertNull($vocabulary->get('description'));
$this->assertSame('', $vocabulary->getDescription());
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
/**
* Tests the representative node relationship for terms.
*
* @group taxonomy
*/
class TaxonomyDefaultArgumentTest extends TaxonomyTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['taxonomy_default_argument_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests escaping of page title when the taxonomy plugin provides it.
*/
public function testTermTitleEscaping(): void {
$this->term1->setName('<em>Markup</em>')->save();
$this->drupalGet('taxonomy_default_argument_test/' . $this->term1->id());
$this->assertSession()->assertEscaped($this->term1->label());
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\views\Views;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the "All terms" taxonomy term field handler.
*
* @group taxonomy
*/
class TaxonomyFieldAllTermsTest extends TaxonomyTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['taxonomy_all_terms_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests the "all terms" field handler.
*/
public function testViewsHandlerAllTermsField(): void {
$this->term1->setName('<em>Markup</em>')->save();
$view = Views::getView('taxonomy_all_terms_test');
$this->executeView($view);
$this->drupalGet('taxonomy_all_terms_test');
// Test term1 links.
$xpath = '//a[@href="' . $this->term1->toUrl()->toString() . '"]';
$this->assertSession()->elementsCount('xpath', $xpath, 2);
$links = $this->xpath($xpath);
$this->assertEquals($this->term1->label(), $links[0]->getText());
$this->assertEquals($this->term1->label(), $links[1]->getText());
$this->assertSession()->assertEscaped($this->term1->label());
// Test term2 links.
$xpath = '//a[@href="' . $this->term2->toUrl()->toString() . '"]';
$this->assertSession()->elementsCount('xpath', $xpath, 2);
$links = $this->xpath($xpath);
$this->assertEquals($this->term2->label(), $links[0]->getText());
$this->assertEquals($this->term2->label(), $links[1]->getText());
}
/**
* Tests token replacement in the "all terms" field handler.
*/
public function testViewsHandlerAllTermsWithTokens(): void {
$view = Views::getView('taxonomy_all_terms_test');
$this->drupalGet('taxonomy_all_terms_token_test');
// Term itself: {{ term_node_tid }}
$this->assertSession()->pageTextContains('Term: ' . $this->term1->getName());
// The taxonomy term ID for the term: {{ term_node_tid__tid }}
$this->assertSession()->pageTextContains('The taxonomy term ID for the term: ' . $this->term1->id());
// The taxonomy term name for the term: {{ term_node_tid__name }}
$this->assertSession()->pageTextContains('The taxonomy term name for the term: ' . $this->term1->getName());
// The machine name for the vocabulary the term belongs to: {{ term_node_tid__vocabulary_vid }}
$this->assertSession()->pageTextContains('The machine name for the vocabulary the term belongs to: ' . $this->term1->bundle());
// The name for the vocabulary the term belongs to: {{ term_node_tid__vocabulary }}
$vocabulary = Vocabulary::load($this->term1->bundle());
$this->assertSession()->pageTextContains('The name for the vocabulary the term belongs to: ' . $vocabulary->label());
}
}

View File

@@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Views;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
/**
* Tests taxonomy field filters with translations.
*
* @group taxonomy
*/
class TaxonomyFieldFilterTest extends ViewTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'language',
'taxonomy',
'taxonomy_test_views',
'text',
'views',
'node',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_field_filters'];
/**
* The vocabulary used for creating terms.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* List of taxonomy term names by language.
*
* @var array
*/
public $termNames = [];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
// Add two new languages.
ConfigurableLanguage::createFromLangcode('fr')->save();
ConfigurableLanguage::createFromLangcode('es')->save();
// Set up term names.
$this->termNames = [
'en' => 'Food in Paris',
'es' => 'Comida en Paris',
'fr' => 'Nourriture en Paris',
];
// Create a vocabulary.
$this->vocabulary = Vocabulary::create([
'name' => 'Views testing tags',
'vid' => 'views_testing_tags',
]);
$this->vocabulary->save();
// Add a translatable field to the vocabulary.
$field = FieldStorageConfig::create([
'field_name' => 'field_foo',
'entity_type' => 'taxonomy_term',
'type' => 'text',
]);
$field->save();
FieldConfig::create([
'field_name' => 'field_foo',
'entity_type' => 'taxonomy_term',
'label' => 'Foo',
'bundle' => 'views_testing_tags',
])->save();
// Create term with translations.
$taxonomy = $this->createTermWithProperties(['name' => $this->termNames['en'], 'langcode' => 'en', 'description' => $this->termNames['en'], 'field_foo' => $this->termNames['en']]);
foreach (['es', 'fr'] as $langcode) {
$translation = $taxonomy->addTranslation($langcode, ['name' => $this->termNames[$langcode]]);
$translation->description->value = $this->termNames[$langcode];
$translation->field_foo->value = $this->termNames[$langcode];
}
$taxonomy->save();
Views::viewsData()->clear();
ViewTestData::createTestViews(static::class, ['taxonomy_test_views']);
}
/**
* Tests description and term name filters.
*/
public function testFilters(): void {
// Test the name filter page, which filters for name contains 'Comida'.
// Should show just the Spanish translation, once.
$this->assertPageCounts('test-name-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida name filter');
// Test the description filter page, which filters for description contains
// 'Comida'. Should show just the Spanish translation, once.
$this->assertPageCounts('test-desc-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida description filter');
// Test the field filter page, which filters for field_foo contains
// 'Comida'. Should show just the Spanish translation, once.
$this->assertPageCounts('test-field-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida field filter');
// Test the name Paris filter page, which filters for name contains
// 'Paris'. Should show each translation once.
$this->assertPageCounts('test-name-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris name filter');
// Test the description Paris page, which filters for description contains
// 'Paris'. Should show each translation, once.
$this->assertPageCounts('test-desc-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris description filter');
// Test the field Paris filter page, which filters for field_foo contains
// 'Paris'. Should show each translation once.
$this->assertPageCounts('test-field-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris field filter');
}
/**
* Asserts that the given taxonomy translation counts are correct.
*
* @param string $path
* Path of the page to test.
* @param array $counts
* Array whose keys are languages, and values are the number of times
* that translation should be shown on the given page.
* @param string $message
* Message suffix to display.
*
* @internal
*/
protected function assertPageCounts(string $path, array $counts, string $message): void {
// Get the text of the page.
$this->drupalGet($path);
$text = $this->getTextContent();
// Check the counts. Note that the title and body are both shown on the
// page, and they are the same. So the title/body string should appear on
// the page twice as many times as the input count.
foreach ($counts as $langcode => $count) {
$this->assertEquals(2 * $count, substr_count($text, $this->termNames[$langcode]), 'Translation ' . $langcode . ' has count ' . $count . ' with ' . $message);
}
}
/**
* Creates a taxonomy term with specified name and other properties.
*
* @param array $properties
* Array of properties and field values to set.
*
* @return \Drupal\taxonomy\TermInterface
* The created taxonomy term.
*/
protected function createTermWithProperties($properties) {
// Use the first available text format.
$filter_formats = filter_formats();
$format = array_pop($filter_formats);
$properties += [
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'field_foo' => $this->randomMachineName(),
];
$term = Term::create([
'name' => $properties['name'],
'description' => $properties['description'],
'format' => $format->id(),
'vid' => $this->vocabulary->id(),
'langcode' => $properties['langcode'],
]);
$term->field_foo->value = $properties['field_foo'];
$term->save();
return $term;
}
}

View File

@@ -0,0 +1,556 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\views_ui\Functional\UITestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Entity\View;
/**
* Tests the taxonomy index filter handler UI.
*
* @group taxonomy
* @group #slow
* @see \Drupal\taxonomy\Plugin\views\field\TaxonomyIndexTid
*/
class TaxonomyIndexTidUiTest extends UITestBase {
use EntityReferenceFieldCreationTrait;
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = [
'test_filter_taxonomy_index_tid',
'test_taxonomy_term_name',
'test_taxonomy_exposed_grouped_filter',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'node',
'taxonomy',
'views',
'views_ui',
'taxonomy_test_views',
];
/**
* A nested array of \Drupal\taxonomy\TermInterface objects.
*
* @var \Drupal\taxonomy\TermInterface[][]
*/
protected $terms = [];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
$this->adminUser = $this->drupalCreateUser([
'administer taxonomy',
'administer views',
]);
$this->drupalLogin($this->adminUser);
Vocabulary::create([
'vid' => 'tags',
'name' => 'Tags',
])->save();
// Setup a hierarchy which looks like this:
// term 0.0
// term 1.0
// - term 1.1
// term 2.0
// - term 2.1
// - term 2.2
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j <= $i; $j++) {
$this->terms[$i][$j] = $term = Term::create([
'vid' => 'tags',
'name' => "Term $i.$j",
'parent' => isset($this->terms[$i][0]) ? $this->terms[$i][0]->id() : 0,
]);
$term->save();
}
}
ViewTestData::createTestViews(static::class, ['taxonomy_test_views']);
// Extra taxonomy and terms.
Vocabulary::create([
'vid' => 'other_tags',
'name' => 'Other tags',
])->save();
$this->terms[3][0] = $term = Term::create([
'vid' => 'tags',
'name' => "Term 3.0",
]);
$term->save();
Vocabulary::create([
'vid' => 'empty_vocabulary',
'name' => 'Empty Vocabulary',
])->save();
}
/**
* Tests the filter UI.
*/
public function testFilterUI(): void {
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
$result = $this->assertSession()->selectExists('edit-options-value')->findAll('css', 'option');
// Ensure that the expected hierarchy is available in the UI.
$counter = 0;
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j <= $i; $j++) {
$option = $result[$counter++];
$prefix = $this->terms[$i][$j]->parent->target_id ? '-' : '';
$tid = $option->getAttribute('value');
$this->assertEquals($prefix . $this->terms[$i][$j]->getName(), $option->getText());
$this->assertEquals($this->terms[$i][$j]->id(), $tid);
}
}
// Ensure the autocomplete input element appears when using the 'textfield'
// type.
$view = View::load('test_filter_taxonomy_index_tid');
$display =& $view->getDisplay('default');
$display['display_options']['filters']['tid']['type'] = 'textfield';
$view->save();
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
$this->assertSession()->fieldExists('edit-options-value');
// Tests \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid::calculateDependencies().
$expected = [
'config' => [
'taxonomy.vocabulary.tags',
],
'content' => [
'taxonomy_term:tags:' . Term::load(2)->uuid(),
],
'module' => [
'node',
'taxonomy',
'user',
],
];
$this->assertSame($expected, $view->calculateDependencies()->getDependencies());
}
/**
* Tests exposed taxonomy filters.
*/
public function testExposedFilter(): void {
$node_type = $this->drupalCreateContentType(['type' => 'page']);
// Create the tag field itself.
$field_name = 'taxonomy_tags';
$this->createEntityReferenceField('node', $node_type->id(), $field_name, NULL, 'taxonomy_term');
// Create 4 nodes: 1 without a term, 2 with the same term, and 1 with a
// different term.
$node1 = $this->drupalCreateNode();
$node2 = $this->drupalCreateNode([
$field_name => [['target_id' => $this->terms[1][0]->id()]],
]);
$node3 = $this->drupalCreateNode([
$field_name => [['target_id' => $this->terms[1][0]->id()]],
]);
$node4 = $this->drupalCreateNode([
$field_name => [['target_id' => $this->terms[2][0]->id()]],
]);
// Only the nodes with the selected term should be shown.
$this->drupalGet('test-filter-taxonomy-index-tid');
$this->assertSession()->pageTextNotContains($node1->getTitle());
$this->assertSession()->linkByHrefNotExists($node1->toUrl()->toString());
$xpath_node2_link = $this->assertSession()->buildXPathQuery('//div[@class="views-row"]//a[@href=:url and text()=:label]', [
':url' => $node2->toUrl()->toString(),
':label' => $node2->label(),
]);
$this->assertSession()->elementsCount('xpath', $xpath_node2_link, 1);
$xpath_node3_link = $this->assertSession()->buildXPathQuery('//div[@class="views-row"]//a[@href=:url and text()=:label]', [
':url' => $node3->toUrl()->toString(),
':label' => $node3->label(),
]);
$this->assertSession()->elementsCount('xpath', $xpath_node3_link, 1);
$this->assertSession()->pageTextNotContains($node4->getTitle());
$this->assertSession()->linkByHrefNotExists($node4->toUrl()->toString());
// Expose the filter.
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
$this->submitForm([], 'Expose filter');
// Set the operator to 'empty' and remove the default term ID.
$this->submitForm([
'options[operator]' => 'empty',
'options[value][]' => [],
], 'Apply');
// Save the view.
$this->submitForm([], 'Save');
// After switching to 'empty' operator, the node without a term should be
// shown.
$this->drupalGet('test-filter-taxonomy-index-tid');
$xpath_node1_link = $this->assertSession()->buildXPathQuery('//div[@class="views-row"]//a[@href=:url and text()=:label]', [
':url' => $node1->toUrl()->toString(),
':label' => $node1->label(),
]);
$this->assertSession()->elementsCount('xpath', $xpath_node1_link, 1);
$this->assertSession()->pageTextNotContains($node2->getTitle());
$this->assertSession()->linkByHrefNotExists($node2->toUrl()->toString());
$this->assertSession()->pageTextNotContains($node3->getTitle());
$this->assertSession()->linkByHrefNotExists($node3->toUrl()->toString());
$this->assertSession()->pageTextNotContains($node4->getTitle());
$this->assertSession()->linkByHrefNotExists($node4->toUrl()->toString());
// Set the operator to 'not empty'.
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
$this->submitForm(['options[operator]' => 'not empty'], 'Apply');
// Save the view.
$this->submitForm([], 'Save');
// After switching to 'not empty' operator, all nodes with terms should be
// shown.
$this->drupalGet('test-filter-taxonomy-index-tid');
$this->assertSession()->pageTextNotContains($node1->getTitle());
$this->assertSession()->linkByHrefNotExists($node1->toUrl()->toString());
$xpath_node2_link = $this->assertSession()->buildXPathQuery('//div[@class="views-row"]//a[@href=:url and text()=:label]', [
':url' => $node2->toUrl()->toString(),
':label' => $node2->label(),
]);
$this->assertSession()->elementsCount('xpath', $xpath_node2_link, 1);
$xpath_node3_link = $this->assertSession()->buildXPathQuery('//div[@class="views-row"]//a[@href=:url and text()=:label]', [
':url' => $node3->toUrl()->toString(),
':label' => $node3->label(),
]);
$this->assertSession()->elementsCount('xpath', $xpath_node3_link, 1);
$xpath_node4_link = $this->assertSession()->buildXPathQuery('//div[@class="views-row"]//a[@href=:url and text()=:label]', [
':url' => $node4->toUrl()->toString(),
':label' => $node4->label(),
]);
$this->assertSession()->elementsCount('xpath', $xpath_node4_link, 1);
// Select 'Term ID' as the field to be displayed.
$edit = ['name[taxonomy_term_field_data.tid]' => TRUE];
$this->drupalGet('admin/structure/views/nojs/add-handler/test_taxonomy_term_name/default/field');
$this->submitForm($edit, 'Add and configure fields');
// Select 'Term' and 'Vocabulary' as filters.
$edit = [
'name[taxonomy_term_field_data.tid]' => TRUE,
'name[taxonomy_term_field_data.vid]' => TRUE,
];
$this->drupalGet('admin/structure/views/nojs/add-handler/test_taxonomy_term_name/default/filter');
$this->submitForm($edit, 'Add and configure filter criteria');
// Select 'Empty Vocabulary' and 'Autocomplete' from the list of options.
$this->drupalGet('admin/structure/views/nojs/handler-extra/test_taxonomy_term_name/default/filter/tid');
$this->submitForm([], 'Apply and continue');
// Expose the filter.
$edit = ['options[expose_button][checkbox][checkbox]' => TRUE];
$this->drupalGet('admin/structure/views/nojs/handler/test_taxonomy_term_name/default/filter/tid');
$this->submitForm($edit, 'Expose filter');
$this->drupalGet('admin/structure/views/nojs/handler/test_taxonomy_term_name/default/filter/tid');
$this->submitForm($edit, 'Apply');
// Filter 'Taxonomy terms' belonging to 'Empty Vocabulary'.
$edit = ['options[value][empty_vocabulary]' => TRUE];
$this->drupalGet('admin/structure/views/nojs/handler/test_taxonomy_term_name/default/filter/vid');
$this->submitForm($edit, 'Apply');
$this->drupalGet('admin/structure/views/view/test_taxonomy_term_name/edit/default');
$this->submitForm([], 'Save');
$this->submitForm([], 'Update preview');
$this->assertSession()->pageTextNotContains($node1->getTitle());
$this->assertSession()->linkByHrefNotExists($node1->toUrl()->toString());
$this->assertSession()->pageTextNotContains($node2->getTitle());
$this->assertSession()->linkByHrefNotExists($node2->toUrl()->toString());
$this->assertSession()->pageTextNotContains($node3->getTitle());
$this->assertSession()->linkByHrefNotExists($node3->toUrl()->toString());
$this->assertSession()->pageTextNotContains($node4->getTitle());
$this->assertSession()->linkByHrefNotExists($node4->toUrl()->toString());
$this->assertSession()->elementNotExists('xpath', "//div[@class='views-row']");
}
/**
* Tests exposed grouped taxonomy filters.
*/
public function testExposedGroupedFilter(): void {
// Create a content type with a taxonomy field.
$this->drupalCreateContentType(['type' => 'article']);
$field_name = 'field_views_testing_tags';
$this->createEntityReferenceField('node', 'article', $field_name, NULL, 'taxonomy_term');
$nodes = [];
for ($i = 0; $i < 3; $i++) {
$node = [];
$node['type'] = 'article';
$node['field_views_testing_tags'][0]['target_id'] = $this->terms[$i][0]->id();
$nodes[] = $this->drupalCreateNode($node);
}
$this->drupalGet('/admin/structure/views/nojs/handler/test_taxonomy_exposed_grouped_filter/page_1/filter/field_views_testing_tags_target_id');
$edit = [
'options[group_info][group_items][1][value][]' => [$this->terms[0][0]->id(), $this->terms[1][0]->id()],
'options[group_info][group_items][2][value][]' => [$this->terms[1][0]->id(), $this->terms[2][0]->id()],
'options[group_info][group_items][3][value][]' => [$this->terms[2][0]->id(), $this->terms[0][0]->id()],
];
$this->submitForm($edit, 'Apply');
$this->submitForm([], 'Save');
// Visit the view's page URL and validate the results.
$this->drupalGet('/test-taxonomy-exposed-grouped-filter');
$this->submitForm(['field_views_testing_tags_target_id' => 1], 'Apply');
$this->assertSession()->pageTextContains($nodes[0]->getTitle());
$this->assertSession()->pageTextContains($nodes[1]->getTitle());
$this->assertSession()->pageTextNotContains($nodes[2]->getTitle());
$this->submitForm(['field_views_testing_tags_target_id' => 2], 'Apply');
$this->assertSession()->pageTextContains($nodes[1]->getTitle());
$this->assertSession()->pageTextContains($nodes[2]->getTitle());
$this->assertSession()->pageTextNotContains($nodes[0]->getTitle());
$this->submitForm(['field_views_testing_tags_target_id' => 3], 'Apply');
$this->assertSession()->pageTextContains($nodes[0]->getTitle());
$this->assertSession()->pageTextContains($nodes[2]->getTitle());
$this->assertSession()->pageTextNotContains($nodes[1]->getTitle());
}
/**
* Tests that an exposed taxonomy filter doesn't show unpublished terms.
*/
public function testExposedUnpublishedFilterOptions(): void {
$this->terms[1][0]->setUnpublished()->save();
// Expose the filter.
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
$this->submitForm([], 'Expose filter');
$edit = ['options[expose_button][checkbox][checkbox]' => TRUE];
$this->submitForm($edit, 'Apply');
$this->submitForm([], 'Save');
// Make sure the unpublished term is shown to the admin user.
$this->drupalGet('test-filter-taxonomy-index-tid');
$this->assertNotEmpty($this->cssSelect('option[value="' . $this->terms[0][0]->id() . '"]'));
$this->assertNotEmpty($this->cssSelect('option[value="' . $this->terms[1][0]->id() . '"]'));
$this->drupalLogout();
$this->drupalGet('test-filter-taxonomy-index-tid');
// Make sure the unpublished term isn't shown to the anonymous user.
$this->assertNotEmpty($this->cssSelect('option[value="' . $this->terms[0][0]->id() . '"]'));
$this->assertEmpty($this->cssSelect('option[value="' . $this->terms[1][0]->id() . '"]'));
// Tests that the term also isn't shown when not showing hierarchy.
$this->drupalLogin($this->adminUser);
$edit = [
'options[hierarchy]' => FALSE,
];
$this->drupalGet('admin/structure/views/nojs/handler-extra/test_filter_taxonomy_index_tid/default/filter/tid');
$this->submitForm($edit, 'Apply');
$this->submitForm([], 'Save');
$this->drupalGet('test-filter-taxonomy-index-tid');
$this->assertNotEmpty($this->cssSelect('option[value="' . $this->terms[0][0]->id() . '"]'));
$this->assertNotEmpty($this->cssSelect('option[value="' . $this->terms[1][0]->id() . '"]'));
$this->drupalLogout();
$this->drupalGet('test-filter-taxonomy-index-tid');
// Make sure the unpublished term isn't shown to the anonymous user.
$this->assertNotEmpty($this->cssSelect('option[value="' . $this->terms[0][0]->id() . '"]'));
$this->assertEmpty($this->cssSelect('option[value="' . $this->terms[1][0]->id() . '"]'));
}
/**
* Tests using the TaxonomyIndexTid in a filter group.
*/
public function testFilterGrouping(): void {
$node_type = $this->drupalCreateContentType(['type' => 'page']);
// Create the tag field itself.
$field_name = 'taxonomy_tags';
$this->createEntityReferenceField('node', $node_type->id(), $field_name, NULL, 'taxonomy_term');
// Create the other tag field itself.
$field_name2 = 'taxonomy_other_tags';
$this->createEntityReferenceField('node', $node_type->id(), $field_name2, NULL, 'taxonomy_term');
// Create 5 nodes: 1 node without any tagging, 2 nodes tagged with 1 term,
// 1 node with 2 tagged terms and 1 with other tags term.
$node_no_term = $this->drupalCreateNode();
$node_with_term_1_0 = $this->drupalCreateNode([
$field_name => [['target_id' => $this->terms[1][0]->id()]],
]);
$node_with_terms_1_0_and_1_1 = $this->drupalCreateNode([
$field_name => [
['target_id' => $this->terms[1][0]->id()],
['target_id' => $this->terms[1][1]->id()],
],
]);
$node_with_term_2_0 = $this->drupalCreateNode([
$field_name => [['target_id' => $this->terms[2][0]->id()]],
]);
$node_with_term_3_0 = $this->drupalCreateNode([
$field_name2 => [['target_id' => $this->terms[3][0]->id()]],
]);
// Create two groups. The first group contains the published filter and set
// up the second group as an 'OR' group. The first subgroup of the second
// filter group will vary as follows:
// - multiple values vs single value
// - not vs or operator values
$view = View::load('test_filter_taxonomy_index_tid');
$display =& $view->getDisplay('default');
// Case 1:
// - filter "tid" with multiple terms as "is none of"
// - filter "tid_2" with a single term as "is one of"
$display['display_options']['filters']['tid']['value'][0] = $this->terms[1][0]->id();
$display['display_options']['filters']['tid']['value'][1] = $this->terms[1][1]->id();
$display['display_options']['filters']['tid']['operator'] = 'not';
$display['display_options']['filters']['tid']['group'] = 2;
$display['display_options']['filters']['tid_2'] = $display['display_options']['filters']['tid'];
$display['display_options']['filters']['tid_2']['id'] = 'tid_2';
$display['display_options']['filters']['tid_2']['value'][0] = $this->terms[2][0]->id();
$display['display_options']['filters']['tid_2']['operator'] = 'or';
$display['display_options']['filters']['tid_2']['group'] = 2;
$display['display_options']['filter_groups'] = [
'operator' => 'AND',
'groups' => [
1 => 'AND',
2 => 'OR',
],
];
$view->save();
$this->drupalGet('test-filter-taxonomy-index-tid');
// We expect no nodes tagged with term 1.0 or 1.1. The node tagged with
// term 2.0 and the untagged node will be shown.
$this->assertSession()->pageTextNotContains($node_with_term_1_0->label());
$this->assertSession()->pageTextNotContains($node_with_terms_1_0_and_1_1->label());
$this->assertSession()->pageTextContainsOnce($node_with_term_2_0->label());
$this->assertSession()->pageTextContainsOnce($node_no_term->label());
// Case 2:
// - filter "tid" with multiple terms as "is one of"
// - filter "tid_2" with a single term as "is one of"
$view = View::load('test_filter_taxonomy_index_tid');
$display =& $view->getDisplay('default');
$display['display_options']['filters']['tid']['value'][0] = $this->terms[1][0]->id();
$display['display_options']['filters']['tid']['value'][1] = $this->terms[1][1]->id();
$display['display_options']['filters']['tid']['operator'] = 'or';
$display['display_options']['filters']['tid']['group'] = 2;
$display['display_options']['filters']['tid_2'] = $display['display_options']['filters']['tid'];
$display['display_options']['filters']['tid_2']['id'] = 'tid_2';
$display['display_options']['filters']['tid_2']['value'][0] = $this->terms[2][0]->id();
$display['display_options']['filters']['tid_2']['operator'] = 'or';
$display['display_options']['filters']['tid_2']['group'] = 2;
$view->save();
$this->drupalGet('test-filter-taxonomy-index-tid');
// We expect all the tagged nodes but not the untagged node.
$this->assertSession()->pageTextContainsOnce($node_with_term_1_0->label());
// The view does not have DISTINCT query enabled, the node tagged with
// both 1.0 and 1.1 will appear twice.
$this->assertSession()->pageTextMatchesCount(2, "/{$node_with_terms_1_0_and_1_1->label()}/");
$this->assertSession()->pageTextContainsOnce($node_with_term_2_0->label());
$this->assertSession()->pageTextNotContains($node_no_term->label());
// Case 3:
// - filter "tid" with a single term as "is none of"
// - filter "tid_2" with a single term as "is one of"
$view = View::load('test_filter_taxonomy_index_tid');
$display =& $view->getDisplay('default');
$display['display_options']['filters']['tid']['value'] = [];
$display['display_options']['filters']['tid']['value'][0] = $this->terms[1][0]->id();
$display['display_options']['filters']['tid']['operator'] = 'not';
$display['display_options']['filters']['tid']['group'] = 2;
$display['display_options']['filters']['tid_2'] = $display['display_options']['filters']['tid'];
$display['display_options']['filters']['tid_2']['id'] = 'tid_2';
$display['display_options']['filters']['tid_2']['value'][0] = $this->terms[2][0]->id();
$display['display_options']['filters']['tid_2']['operator'] = 'or';
$display['display_options']['filters']['tid_2']['group'] = 2;
$view->save();
$this->drupalGet('test-filter-taxonomy-index-tid');
// We expect none of the nodes tagged with term 1.0. The node tagged with
// term 2.0 and the untagged node should be shown.
$this->assertSession()->pageTextNotContains($node_with_term_1_0->label());
$this->assertSession()->pageTextNotContains($node_with_terms_1_0_and_1_1->label());
$this->assertSession()->pageTextContainsOnce($node_with_term_2_0->label());
$this->assertSession()->pageTextContainsOnce($node_no_term->label());
// Case 4:
// - filter "tid" with a single term as "is one of"
// - filter "tid_2" with a single term as "is one of"
$view = View::load('test_filter_taxonomy_index_tid');
$display =& $view->getDisplay('default');
$display['display_options']['filters']['tid']['value'] = [];
$display['display_options']['filters']['tid']['value'][0] = $this->terms[1][0]->id();
$display['display_options']['filters']['tid']['operator'] = 'or';
$display['display_options']['filters']['tid']['group'] = 2;
$display['display_options']['filters']['tid_2'] = $display['display_options']['filters']['tid'];
$display['display_options']['filters']['tid_2']['id'] = 'tid_2';
$display['display_options']['filters']['tid_2']['value'][0] = $this->terms[2][0]->id();
$view->save();
$this->drupalGet('test-filter-taxonomy-index-tid');
// We expect all the tagged nodes to be shown but not the untagged node.
$this->assertSession()->pageTextContainsOnce($node_with_term_1_0->label());
$this->assertSession()->pageTextContainsOnce($node_with_terms_1_0_and_1_1->label());
$this->assertSession()->pageTextContainsOnce($node_with_term_2_0->label());
$this->assertSession()->pageTextNotContains($node_no_term->label());
// Different fields/taxonomies filters/values.
// Case 5: OR
// - filter "tid" with terms from tags as "is one of"
// - filter "taxonomy_other_tags_target_id" with term from other tags
// as "is one of".
$view = View::load('test_filter_taxonomy_index_tid');
$display = &$view->getDisplay('default');
$display['display_options']['filters']['tid']['value'][0] = $this->terms[1][0]->id();
$display['display_options']['filters']['tid']['value'][1] = $this->terms[1][1]->id();
$display['display_options']['filters']['tid']['operator'] = 'or';
$display['display_options']['filters']['tid']['group'] = 2;
$display['display_options']['filters']['taxonomy_other_tags_target_id'] = $display['display_options']['filters']['tid'];
$display['display_options']['filters']['taxonomy_other_tags_target_id']['id'] = 'taxonomy_other_tags_target_id';
$display['display_options']['filters']['taxonomy_other_tags_target_id']['value'][0] = $this->terms[3][0]->id();
$display['display_options']['filters']['taxonomy_other_tags_target_id']['operator'] = 'or';
$display['display_options']['filters']['taxonomy_other_tags_target_id']['group'] = 2;
$display['display_options']['filters']['taxonomy_other_tags_target_id']['table'] = 'node__taxonomy_other_tags';
$display['display_options']['filters']['taxonomy_other_tags_target_id']['field'] = 'taxonomy_other_tags_target_id';
unset($display['display_options']['filters']['tid_2']);
$display['display_options']['filter_groups'] = [
'operator' => 'AND',
'groups' => [
1 => 'AND',
2 => 'OR',
],
];
$view->save();
$this->drupalGet('test-filter-taxonomy-index-tid');
// We expect no nodes tagged with term 1.0 or 1.1. The node tagged with
// term 3.0 and the untagged node will be shown.
$this->assertSession()->pageTextContainsOnce($node_with_term_1_0->label());
// The view does not have DISTINCT query enabled, the node tagged with
// both 1.0 and 1.1 will appear twice.
$this->assertSession()->pageTextMatchesCount(2, "/{$node_with_terms_1_0_and_1_1->label()}/");
$this->assertSession()->pageTextContainsOnce($node_with_term_3_0->label());
$this->assertSession()->pageTextNotContains($node_with_term_2_0->label());
$this->assertSession()->pageTextNotContains($node_no_term->label());
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Tests\views_ui\Functional\UITestBase;
/**
* Tests views taxonomy parent plugin UI.
*
* @group taxonomy
* @see Drupal\taxonomy\Plugin\views\access\Role
*/
class TaxonomyParentUITest extends UITestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_taxonomy_parent'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy', 'taxonomy_test_views'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = ['taxonomy_test_views']): void {
parent::setUp($import_test_views, $modules);
}
/**
* Tests the taxonomy parent plugin UI.
*/
public function testTaxonomyParentUI(): void {
$this->drupalGet('admin/structure/views/nojs/handler/test_taxonomy_parent/default/relationship/parent');
$this->assertSession()->pageTextNotContains('The handler for this item is broken or missing.');
}
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\TermInterface;
use Drupal\views\Views;
/**
* Tests taxonomy relationships with parent term and node.
*
* @group taxonomy
*/
class TaxonomyRelationshipTest extends TaxonomyTestBase {
/**
* Stores the terms used in the tests.
*
* @var \Drupal\taxonomy\TermInterface[]
*/
protected $terms = [];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_taxonomy_term_relationship'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
// Make term2 parent of term1.
$this->term1->set('parent', $this->term2->id());
$this->term1->save();
// Store terms in an array for testing.
$this->terms[] = $this->term1;
$this->terms[] = $this->term2;
// Only set term1 on node1 and term2 on node2 for testing.
unset($this->nodes[0]->field_views_testing_tags[1]);
$this->nodes[0]->save();
unset($this->nodes[1]->field_views_testing_tags[0]);
$this->nodes[1]->save();
Views::viewsData()->clear();
}
/**
* Tests the taxonomy parent plugin UI.
*/
public function testTaxonomyRelationships(): void {
// Check the generated views data of taxonomy_index.
$views_data = Views::viewsData()->get('taxonomy_index');
// Check the table join data.
$this->assertEquals('tid', $views_data['table']['join']['taxonomy_term_field_data']['left_field']);
$this->assertEquals('tid', $views_data['table']['join']['taxonomy_term_field_data']['field']);
$this->assertEquals('nid', $views_data['table']['join']['node_field_data']['left_field']);
$this->assertEquals('nid', $views_data['table']['join']['node_field_data']['field']);
$this->assertEquals('entity_id', $views_data['table']['join']['taxonomy_term__parent']['left_field']);
$this->assertEquals('tid', $views_data['table']['join']['taxonomy_term__parent']['field']);
// Check the generated views data of taxonomy_term__parent.
$views_data = Views::viewsData()->get('taxonomy_term__parent');
// Check the table join data.
$this->assertEquals('entity_id', $views_data['table']['join']['taxonomy_term__parent']['left_field']);
$this->assertEquals('parent_target_id', $views_data['table']['join']['taxonomy_term__parent']['field']);
$this->assertEquals('tid', $views_data['table']['join']['taxonomy_term_field_data']['left_field']);
$this->assertEquals('entity_id', $views_data['table']['join']['taxonomy_term_field_data']['field']);
// Check the parent relationship data.
$this->assertEquals('taxonomy_term_field_data', $views_data['parent_target_id']['relationship']['base']);
$this->assertEquals('tid', $views_data['parent_target_id']['relationship']['base field']);
$this->assertEquals('Parent', $views_data['parent_target_id']['relationship']['label']);
$this->assertEquals('standard', $views_data['parent_target_id']['relationship']['id']);
// Check the parent filter and argument data.
$this->assertEquals('numeric', $views_data['parent_target_id']['filter']['id']);
$this->assertEquals('taxonomy', $views_data['parent_target_id']['argument']['id']);
// Check an actual test view.
$view = Views::getView('test_taxonomy_term_relationship');
$this->executeView($view);
/** @var \Drupal\views\ResultRow $row */
foreach ($view->result as $index => $row) {
// Check that the actual ID of the entity is the expected one.
$this->assertEquals($this->terms[$index]->id(), $row->tid);
// Also check that we have the correct result entity.
$this->assertEquals($this->terms[$index]->id(), $row->_entity->id());
$this->assertInstanceOf(TermInterface::class, $row->_entity);
if (!$index) {
$this->assertInstanceOf(TermInterface::class, $row->_relationship_entities['parent']);
$this->assertEquals($this->term2->id(), $row->_relationship_entities['parent']->id());
$this->assertEquals($this->term2->id(), $row->taxonomy_term_field_data_taxonomy_term__parent_tid);
}
$this->assertInstanceOf(NodeInterface::class, $row->_relationship_entities['nid']);
$this->assertEquals($this->nodes[$index]->id(), $row->_relationship_entities['nid']->id());
}
// Test node fields are available through relationship.
\Drupal::service('module_installer')->install(['views_ui']);
$this->drupalLogin($this->createUser(['administer views']));
$this->drupalGet('admin/structure/views/view/test_taxonomy_term_relationship');
$this->click('#views-add-field');
$this->assertSession()->pageTextContains('Body');
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
/**
* Tests the taxonomy term with depth argument.
*
* @group taxonomy
*/
class TaxonomyTermArgumentDepthTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'taxonomy',
'taxonomy_test_views',
'views',
'node',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
public static $testViews = ['test_argument_taxonomy_index_tid_depth'];
/**
* @var \Drupal\taxonomy\TermInterface[]
*/
protected $terms = [];
/**
* @var \Drupal\views\ViewExecutable
*/
protected $view;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
// Create a term with markup in the label.
$first = $this->createTerm(['name' => '<em>First</em>']);
// Create a node w/o any terms.
$settings = ['type' => 'article'];
// Create a node with linked to the term.
$settings['field_views_testing_tags'][0]['target_id'] = $first->id();
$this->nodes[] = $this->drupalCreateNode($settings);
$this->terms[0] = $first;
}
/**
* Tests title escaping.
*/
public function testTermWithDepthArgumentTitleEscaping(): void {
$this->drupalGet('test_argument_taxonomy_index_tid_depth/' . $this->terms[0]->id());
$this->assertSession()->assertEscaped($this->terms[0]->label());
}
}

View File

@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use Drupal\views\Views;
/**
* Tests the taxonomy term view page and its translation.
*
* @group taxonomy
*/
class TaxonomyTermViewTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy', 'views'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* A user with permissions to administer taxonomy.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* Name of the taxonomy term reference field.
*
* @var string
*/
protected $fieldName1;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
// Create an administrative user.
$this->adminUser = $this->drupalCreateUser([
'administer taxonomy',
'bypass node access',
]);
$this->drupalLogin($this->adminUser);
// Create a vocabulary and add two term reference fields to article nodes.
$this->fieldName1 = $this->randomMachineName();
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $this->fieldName1, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($this->fieldName1, [
'type' => 'options_select',
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($this->fieldName1, [
'type' => 'entity_reference_label',
])
->save();
}
/**
* Tests that the taxonomy term view is working properly.
*/
public function testTaxonomyTermView(): void {
// Create terms in the vocabulary.
$term = $this->createTerm();
// Post an article.
$edit = [];
$edit['title[0][value]'] = $original_title = $this->randomMachineName();
$edit['body[0][value]'] = $this->randomMachineName();
$edit["{$this->fieldName1}[]"] = $term->id();
$this->drupalGet('node/add/article');
$this->submitForm($edit, 'Save');
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->pageTextContains($term->label());
$this->assertSession()->pageTextContains($node->label());
\Drupal::service('module_installer')->install(['language', 'content_translation']);
ConfigurableLanguage::createFromLangcode('ur')->save();
// Enable translation for the article content type and ensure the change is
// picked up.
\Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
$roles = $this->adminUser->getRoles(TRUE);
Role::load(reset($roles))
->grantPermission('create content translations')
->grantPermission('translate any entity')
->save();
$edit['title[0][value]'] = $translated_title = $this->randomMachineName();
$this->drupalGet('node/' . $node->id() . '/translations/add/en/ur');
$this->submitForm($edit, 'Save (this translation)');
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->pageTextContains($term->label());
$this->assertSession()->pageTextContains($original_title);
$this->assertSession()->pageTextNotContains($translated_title);
$this->drupalGet('ur/taxonomy/term/' . $term->id());
$this->assertSession()->pageTextContains($term->label());
$this->assertSession()->pageTextNotContains($original_title);
$this->assertSession()->pageTextContains($translated_title);
// Uninstall language module and ensure that the language is not part of the
// query anymore.
// @see \Drupal\views\Plugin\views\filter\LanguageFilter::query()
$node->delete();
// We also have to remove the nodes created by the parent ::setUp() method
// if we want to be able to uninstall the Content Translation module.
foreach (Node::loadMultiple() as $node) {
$node->delete();
}
\Drupal::service('module_installer')->uninstall(['content_translation', 'language']);
$view = Views::getView('taxonomy_term');
$view->initDisplay();
$view->setArguments([$term->id()]);
$view->build();
/** @var \Drupal\Core\Database\Query\Select $query */
$query = $view->build_info['query'];
$tables = $query->getTables();
// Ensure that the join to node_field_data is not added by default.
$this->assertEquals(['node_field_data', 'taxonomy_index'], array_keys($tables));
// Ensure that the filter to the language column is not there by default.
$condition = $query->conditions();
// We only want to check the no. of conditions in the query.
unset($condition['#conjunction']);
$this->assertCount(1, $condition);
// Clear permissions for anonymous users to check access for default views.
Role::load(RoleInterface::ANONYMOUS_ID)->revokePermission('access content')->save();
// Test the default views disclose no data by default.
$this->drupalLogout();
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertSession()->statusCodeEquals(403);
$this->drupalGet('taxonomy/term/' . $term->id() . '/feed');
$this->assertSession()->statusCodeEquals(403);
}
}

View File

@@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
use Drupal\views\Tests\ViewTestData;
/**
* Base class for all taxonomy tests.
*/
abstract class TaxonomyTestBase extends ViewTestBase {
use EntityReferenceFieldCreationTrait;
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['taxonomy', 'taxonomy_test_views'];
/**
* Stores the nodes used for the different tests.
*
* @var \Drupal\node\NodeInterface[]
*/
protected $nodes = [];
/**
* The vocabulary used for creating terms.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* Stores the first term used in the different tests.
*
* @var \Drupal\taxonomy\TermInterface
*/
protected $term1;
/**
* Stores the second term used in the different tests.
*
* @var \Drupal\taxonomy\TermInterface
*/
protected $term2;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
// Important: taxonomy_test_views module must not be in the $modules to
// avoid an issue that particular view is already exists.
parent::setUp($import_test_views, $modules);
$this->mockStandardInstall();
// This needs to be done again after ::mockStandardInstall() to make
// test vocabularies available.
// Explicitly add taxonomy_test_views to $modules now, so required views are
// being created.
$modules[] = 'taxonomy_test_views';
if ($import_test_views) {
ViewTestData::createTestViews(static::class, $modules);
}
$this->term1 = $this->createTerm();
$this->term2 = $this->createTerm();
$node = [];
$node['type'] = 'article';
$node['field_views_testing_tags'][]['target_id'] = $this->term1->id();
$node['field_views_testing_tags'][]['target_id'] = $this->term2->id();
$this->nodes[] = $this->drupalCreateNode($node);
$this->nodes[] = $this->drupalCreateNode($node);
}
/**
* Provides a workaround for the inability to use the standard profile.
*
* @see https://www.drupal.org/node/1708692
*/
protected function mockStandardInstall() {
$this->drupalCreateContentType([
'type' => 'article',
]);
// Create the vocabulary for the tag field.
$this->vocabulary = Vocabulary::create([
'name' => 'Views testing tags',
'vid' => 'views_testing_tags',
]);
$this->vocabulary->save();
$field_name = 'field_' . $this->vocabulary->id();
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
],
'auto_create' => TRUE,
];
$this->createEntityReferenceField('node', 'article', $field_name, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display_repository->getFormDisplay('node', 'article')
->setComponent($field_name, [
'type' => 'entity_reference_autocomplete_tags',
'weight' => -4,
])
->save();
$display_repository->getViewDisplay('node', 'article')
->setComponent($field_name, [
'type' => 'entity_reference_label',
'weight' => 10,
])
->save();
$display_repository->getViewDisplay('node', 'article', 'teaser')
->setComponent($field_name, [
'type' => 'entity_reference_label',
'weight' => 10,
])
->save();
}
/**
* Creates and returns a taxonomy term.
*
* @param array $settings
* (optional) An array of values to override the following default
* properties of the term:
* - name: A random string.
* - description: A random string.
* - format: First available text format.
* - vid: Vocabulary ID of self::$vocabulary object.
* - langcode: LANGCODE_NOT_SPECIFIED.
* Defaults to an empty array.
*
* @return \Drupal\taxonomy\Entity\Term
* The created taxonomy term.
*/
protected function createTerm(array $settings = []) {
$filter_formats = filter_formats();
$format = array_pop($filter_formats);
$settings += [
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
// Use the first available text format.
'format' => $format->id(),
'vid' => $this->vocabulary->id(),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
];
$term = Term::create($settings);
$term->save();
return $term;
}
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the vocabulary argument.
*
* @group taxonomy
*/
class TaxonomyVocabularyArgumentTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'taxonomy_test_views', 'views'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
public static $testViews = ['test_argument_taxonomy_vocabulary'];
/**
* @var \Drupal\taxonomy\TermInterface[]
*/
protected $terms = [];
/**
* Vocabularies used for creating terms.
*
* @var \Drupal\taxonomy\VocabularyInterface[]
*/
protected $vocabularies;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
// Add default vocabulary to list of vocabularies.
$this->vocabularies[] = $this->vocabulary;
// Create additional vocabulary.
$vocabulary = Vocabulary::create([
'name' => 'Views testing category',
'vid' => 'views_testing_category',
]);
$vocabulary->save();
$this->vocabularies[] = $vocabulary;
// Create some terms.
$this->terms[0] = $this->createTerm([
'name' => 'First',
'vid' => $this->vocabularies[0]->id(),
]);
$this->terms[1] = $this->createTerm([
'name' => 'Second',
'vid' => $this->vocabularies[1]->id(),
]);
}
/**
* Tests the vocabulary argument handler.
*
* @see Drupal\taxonomy\Plugin\views\argument\VocabularyVid
*/
public function testTermWithVocabularyArgument(): void {
$this->drupalGet('test_argument_taxonomy_vocabulary/' . $this->vocabularies[0]->id());
// First term should be present.
$this->assertSession()->pageTextContains($this->terms[0]->label());
// Second term should not be present.
$this->assertSession()->pageTextNotContains($this->terms[1]->label());
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
/**
* Tests making taxonomy term base fields' displays configurable.
*
* @group taxonomy
*/
class TermDisplayConfigurableTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_term_show_entity'];
/**
* Sets base fields to configurable display and check settings are respected.
*/
public function testDisplayConfigurable(): void {
$user = $this->drupalCreateUser(['administer nodes']);
$this->drupalLogin($user);
$assert = $this->assertSession();
// Check the taxonomy_term with default non-configurable display.
$this->drupalGet('test_term_show_entity');
// Name should be linked to entity and description should be displayed.
$assert->pageTextContains($this->term1->getName());
$assert->linkByHrefExists($this->term1->toUrl()->toString());
$assert->pageTextContains($this->term1->getDescription());
$assert->pageTextContains($this->term2->getName());
$assert->linkByHrefExists($this->term2->toUrl()->toString());
$assert->pageTextContains($this->term2->getDescription());
// The field labels should not be present.
$assert->pageTextNotContains('Name');
$assert->pageTextNotContains('Description');
// Enable helper module to make base fields' displays configurable.
\Drupal::service('module_installer')->install(['taxonomy_term_display_configurable_test']);
// Configure display.
$display = \Drupal::service('entity_display.repository')->getViewDisplay('taxonomy_term', $this->vocabulary->id(), 'default');
$display->setComponent('name', [
'type' => 'text_default',
'label' => 'above',
])->save();
// Recheck the taxonomy_term with configurable display.
$this->drupalGet('test_term_show_entity');
// The description should be the first field in each row, with no label.
// Name field should be the second field in view row. Value should be
// displayed after the label. It should not be linked to the term.
$assert->pageTextContains('Name');
$assert->pageTextNotContains('Description');
$assert->pageTextContains($this->term1->getName());
$assert->linkByHrefNotExists($this->term1->toUrl()->toString());
$assert->pageTextContains($this->term1->getDescription());
$assert->elementTextContains('xpath', '//*[@class="views-row"][1]/div/div[1]//p', $this->term1->getDescription());
$assert->elementTextContains('xpath', '//*[@class="views-row"][1]/div/div[2]/div[1]', 'Name');
$assert->elementTextContains('xpath', '//*[@class="views-row"][1]/div/div[2]/div[2]', $this->term1->getName());
$assert->pageTextContains($this->term2->getName());
$assert->linkByHrefNotExists($this->term2->toUrl()->toString());
$assert->pageTextContains($this->term2->getDescription());
$assert->elementTextContains('xpath', '//*[@class="views-row"][2]/div/div[1]//p', $this->term2->getDescription());
$assert->elementTextContains('xpath', '//*[@class="views-row"][2]/div/div[2]/div[1]', 'Name');
$assert->elementTextContains('xpath', '//*[@class="views-row"][2]/div/div[2]/div[2]', $this->term2->getName());
// Remove 'name' field from display.
$display->removeComponent('name')->save();
// Recheck the taxonomy_term with 'name' field removed from display.
// There should just be an unlabelled description. Nothing should be
// linked to the terms.
$this->drupalGet('test_term_show_entity');
$assert->pageTextNotContains('Name');
$assert->pageTextNotContains('Description');
$assert->pageTextNotContains($this->term1->getName());
$assert->linkByHrefNotExists($this->term1->toUrl()->toString());
$assert->pageTextContains($this->term1->getDescription());
$assert->pageTextNotContains($this->term2->getName());
$assert->linkByHrefNotExists($this->term2->toUrl()->toString());
$assert->pageTextContains($this->term2->getDescription());
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Core\Link;
use Drupal\views\Views;
/**
* Tests the term_name field handler.
*
* @group taxonomy
*
* @see \Drupal\taxonomy\Plugin\views\field\TermName
*/
class TermNameFieldTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
public static $testViews = ['test_taxonomy_term_name'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests term name field plugin functionality.
*/
public function testTermNameField(): void {
$this->term1->name->value = $this->randomMachineName() . ' ' . $this->randomMachineName();
$this->term1->save();
$user = $this->drupalCreateUser(['access content']);
$this->drupalLogin($user);
$view = Views::getView('test_taxonomy_term_name');
$view->initDisplay();
$this->executeView($view);
$this->assertEquals($this->term1->getName(), $view->getStyle()->getField(0, 'name'));
$this->assertEquals($this->term2->getName(), $view->getStyle()->getField(1, 'name'));
$view = Views::getView('test_taxonomy_term_name');
$display =& $view->storage->getDisplay('default');
$display['display_options']['fields']['name']['convert_spaces'] = TRUE;
$view->storage->invalidateCaches();
$this->executeView($view);
$this->assertEquals(str_replace(' ', '-', $this->term1->getName()), $view->getStyle()->getField(0, 'name'));
$this->assertEquals($this->term2->getName(), $view->getStyle()->getField(1, 'name'));
// Enable link_to_entity option and ensure that title is displayed properly.
$view = Views::getView('test_taxonomy_term_name');
$display =& $view->storage->getDisplay('default');
$display['display_options']['fields']['name']['convert_spaces'] = TRUE;
$display['display_options']['fields']['name']['settings']['link_to_entity'] = TRUE;
$view->storage->invalidateCaches();
$this->executeView($view);
$expected_link1 = Link::fromTextAndUrl(str_replace(' ', '-', $this->term1->getName()), $this->term1->toUrl());
$expected_link2 = Link::fromTextAndUrl($this->term2->getName(), $this->term2->toUrl());
$this->assertEquals($expected_link1->toString(), $view->getStyle()->getField(0, 'name'));
$this->assertEquals($expected_link2->toString(), $view->getStyle()->getField(1, 'name'));
}
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Views;
use Drupal\Core\Url;
use Drupal\Tests\taxonomy\Functional\TaxonomyTranslationTestTrait;
/**
* Tests for views translation.
*
* @group taxonomy
*/
class TermTranslationViewsTest extends TaxonomyTestBase {
use TaxonomyTranslationTestTrait;
/**
* Term to translated term mapping.
*
* @var array
*/
protected $termTranslationMap = [
'one' => 'translatedOne',
'two' => 'translatedTwo',
'three' => 'translatedThree',
];
/**
* Created terms.
*
* @var \Drupal\taxonomy\Entity\Term[]
*/
protected $terms = [];
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'language', 'content_translation', 'views'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['taxonomy_translated_term_name_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Language object.
*
* @var \Drupal\Core\Language\LanguageInterface|null
*/
protected $translationLanguage;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
$this->setupLanguages();
$this->enableTranslation();
$this->setUpTerms();
$this->translationLanguage = \Drupal::languageManager()->getLanguage($this->translateToLangcode);
}
/**
* Ensure that proper translation is returned when contextual filter.
*
* Taxonomy term: Term ID & Content: Has taxonomy term ID (with depth)
* contextual filters are enabled for two separate view modes.
*/
public function testTermsTranslationWithContextualFilter(): void {
$this->drupalLogin($this->rootUser);
foreach ($this->terms as $term) {
// Test with "Content: Has taxonomy term ID (with depth)" contextual filter.
// Generate base language url and send request.
$url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_1', ['arg_0' => $term->id()])->toString();
$this->drupalGet($url);
$this->assertSession()->pageTextContains($term->label());
// Generate translation URL and send request.
$url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_1', ['arg_0' => $term->id()], ['language' => $this->translationLanguage])->toString();
$this->drupalGet($url);
$this->assertSession()->pageTextContains($this->termTranslationMap[$term->label()]);
// Test with "Taxonomy term: Term ID" contextual filter.
// Generate base language url and send request.
$url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_2', ['arg_0' => $term->id()])->toString();
$this->drupalGet($url);
$this->assertSession()->pageTextContains($term->label());
// Generate translation URL and send request.
$url = Url::fromRoute('view.taxonomy_translated_term_name_test.page_2', ['arg_0' => $term->id()], ['language' => $this->translationLanguage])->toString();
$this->drupalGet($url);
$this->assertSession()->pageTextContains($this->termTranslationMap[$term->label()]);
}
}
/**
* Setup translated terms in a hierarchy.
*/
protected function setUpTerms() {
$parent_vid = 0;
foreach ($this->termTranslationMap as $name => $translation) {
$term = $this->createTerm([
'name' => $name,
'langcode' => $this->baseLangcode,
'parent' => $parent_vid,
'vid' => $this->vocabulary->id(),
]);
$term->addTranslation($this->translateToLangcode, [
'name' => $translation,
]);
$term->save();
// Each term is nested under the last.
$parent_vid = $term->id();
$this->terms[] = $term;
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Entity\ContentLanguageSettings;
/**
* Tests the language functionality for vocabularies.
*
* @group taxonomy
*/
class VocabularyLanguageTest extends TaxonomyTestBase {
protected static $modules = ['language'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create an administrative user.
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
// Add some custom languages.
ConfigurableLanguage::create([
'id' => 'aa',
'label' => $this->randomMachineName(),
])->save();
ConfigurableLanguage::create([
'id' => 'bb',
'label' => $this->randomMachineName(),
])->save();
}
/**
* Tests language settings for vocabularies.
*/
public function testVocabularyLanguage(): void {
$this->drupalGet('admin/structure/taxonomy/add');
// Check that we have the language selector available.
$this->assertSession()->fieldExists('edit-langcode');
// Create the vocabulary.
$vid = $this->randomMachineName();
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['langcode'] = 'aa';
$edit['vid'] = $vid;
$this->submitForm($edit, 'Save');
// Check the language on the edit page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
$this->assertTrue($this->assertSession()->optionExists('edit-langcode', $edit['langcode'])->isSelected());
// Change the language and save again.
$edit['langcode'] = 'bb';
unset($edit['vid']);
$this->submitForm($edit, 'Save');
// Check again the language on the edit page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
$this->assertTrue($this->assertSession()->optionExists('edit-langcode', $edit['langcode'])->isSelected());
}
/**
* Tests term language settings for vocabulary terms are saved and updated.
*/
public function testVocabularyDefaultLanguageForTerms(): void {
// Add a new vocabulary and check that the default language settings are for
// the terms are saved.
$edit = [
'name' => $this->randomMachineName(),
'vid' => $this->randomMachineName(),
'default_language[langcode]' => 'bb',
'default_language[language_alterable]' => TRUE,
];
$vid = $edit['vid'];
$this->drupalGet('admin/structure/taxonomy/add');
$this->submitForm($edit, 'Save');
// Check that the vocabulary was actually created.
$this->drupalGet('admin/structure/taxonomy/manage/' . $edit['vid']);
$this->assertSession()->statusCodeEquals(200);
// Check that the language settings were saved.
$language_settings = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $edit['vid']);
$this->assertEquals('bb', $language_settings->getDefaultLangcode(), 'The langcode was saved.');
$this->assertTrue($language_settings->isLanguageAlterable(), 'The visibility setting was saved.');
// Check that the correct options are selected in the interface.
$this->assertTrue($this->assertSession()->optionExists('edit-default-language-langcode', 'bb')->isSelected());
$this->assertSession()->checkboxChecked('edit-default-language-language-alterable');
// Edit the vocabulary and check that the new settings are updated.
$edit = [
'default_language[langcode]' => 'aa',
'default_language[language_alterable]' => FALSE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
$this->submitForm($edit, 'Save');
// And check again the settings and also the interface.
$language_settings = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vid);
$this->assertEquals('aa', $language_settings->getDefaultLangcode(), 'The langcode was saved.');
$this->assertFalse($language_settings->isLanguageAlterable(), 'The visibility setting was saved.');
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
$this->assertTrue($this->assertSession()->optionExists('edit-default-language-langcode', 'aa')->isSelected());
$this->assertSession()->checkboxNotChecked('edit-default-language-language-alterable');
// Check that language settings are changed after editing vocabulary.
$edit = [
'name' => $this->randomMachineName(),
'default_language[langcode]' => 'authors_default',
'default_language[language_alterable]' => FALSE,
];
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
$this->submitForm($edit, 'Save');
// Check that we have the new settings.
$new_settings = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vid);
$this->assertEquals('authors_default', $new_settings->getDefaultLangcode(), 'The langcode was saved.');
$this->assertFalse($new_settings->isLanguageAlterable(), 'The new visibility setting was saved.');
}
}

View File

@@ -0,0 +1,394 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Component\Utility\Unicode;
/**
* Tests the taxonomy vocabulary permissions.
*
* @group taxonomy
*/
class VocabularyPermissionsTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['help'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('page_title_block');
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('help_block');
}
/**
* Create, edit and delete a vocabulary via the user interface.
*/
public function testVocabularyPermissionsVocabulary(): void {
// VocabularyTest.php already tests for user with "administer taxonomy"
// permission.
// Test as user without proper permissions.
$authenticated_user = $this->drupalCreateUser([]);
$this->drupalLogin($authenticated_user);
$assert_session = $this->assertSession();
// Visit the main taxonomy administration page.
$this->drupalGet('admin/structure/taxonomy');
$assert_session->statusCodeEquals(403);
// Test as user with "access taxonomy overview" permissions.
$proper_user = $this->drupalCreateUser(['access taxonomy overview']);
$this->drupalLogin($proper_user);
// Visit the main taxonomy administration page.
$this->drupalGet('admin/structure/taxonomy');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('Vocabulary name');
$assert_session->linkNotExists('Add vocabulary');
}
/**
* Tests the vocabulary overview permission.
*/
public function testTaxonomyVocabularyOverviewPermissions(): void {
// Create two vocabularies, one with two terms, the other without any term.
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary1 , $vocabulary2 */
$vocabulary1 = $this->createVocabulary();
$vocabulary2 = $this->createVocabulary();
$vocabulary1_id = $vocabulary1->id();
$vocabulary2_id = $vocabulary2->id();
$this->createTerm($vocabulary1);
$this->createTerm($vocabulary1);
// Assert expected help texts on first vocabulary.
$vocabulary1_label = Unicode::ucfirst($vocabulary1->label());
$edit_help_text = "You can reorganize the terms in $vocabulary1_label using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.";
$no_edit_help_text = "$vocabulary1_label contains the following terms.";
$assert_session = $this->assertSession();
// Logged in as admin user with 'administer taxonomy' permission.
$admin_user = $this->drupalCreateUser(['administer taxonomy']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->linkExists('Edit');
$assert_session->linkExists('Delete');
$assert_session->linkExists('Add term');
$assert_session->buttonExists('Save');
$assert_session->pageTextContains('Weight');
$assert_session->fieldExists('Weight');
$assert_session->pageTextContains($edit_help_text);
$this->submitForm([], 'Reset to alphabetical');
$assert_session->statusCodeEquals(200);
// Visit vocabulary overview without terms. 'Add term' should be shown.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('No terms available');
$assert_session->linkExists('Add term');
// Login as a user without any of the required permissions.
$no_permission_user = $this->drupalCreateUser();
$this->drupalLogin($no_permission_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(403);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(403);
// Log in as a user with only the overview permission, neither edit nor
// delete operations must be available and no Save button.
$overview_only_user = $this->drupalCreateUser(['access taxonomy overview']);
$this->drupalLogin($overview_only_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->linkNotExists('Edit');
$assert_session->linkNotExists('Delete');
$assert_session->buttonNotExists('Save');
$assert_session->buttonNotExists('Reset to alphabetical');
$assert_session->pageTextContains('Weight');
$assert_session->fieldNotExists('Weight');
$assert_session->linkNotExists('Add term');
$assert_session->pageTextContains($no_edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('No terms available');
$assert_session->linkNotExists('Add term');
// Login as a user with permission to edit terms, only edit link should be
// visible.
$edit_user = $this->createUser([
'access taxonomy overview',
'edit terms in ' . $vocabulary1_id,
'edit terms in ' . $vocabulary2_id,
]);
$this->drupalLogin($edit_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->linkExists('Edit');
$assert_session->linkNotExists('Delete');
$assert_session->buttonExists('Save');
$assert_session->pageTextContains('Weight');
$assert_session->fieldExists('Weight');
$assert_session->linkNotExists('Add term');
$assert_session->pageTextContains($edit_help_text);
$this->submitForm([], 'Reset to alphabetical');
$assert_session->statusCodeEquals(200);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('No terms available');
$assert_session->linkNotExists('Add term');
// Login as a user with permission only to delete terms.
$edit_delete_user = $this->createUser([
'access taxonomy overview',
'delete terms in ' . $vocabulary1_id,
'delete terms in ' . $vocabulary2_id,
]);
$this->drupalLogin($edit_delete_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->linkNotExists('Edit');
$assert_session->linkExists('Delete');
$assert_session->linkNotExists('Add term');
$assert_session->buttonNotExists('Save');
$assert_session->buttonNotExists('Reset to alphabetical');
$assert_session->pageTextContains('Weight');
$assert_session->fieldNotExists('Weight');
$assert_session->pageTextContains($no_edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('No terms available');
$assert_session->linkNotExists('Add term');
// Login as a user with permission to edit and delete terms.
$edit_delete_user = $this->createUser([
'access taxonomy overview',
'edit terms in ' . $vocabulary1_id,
'delete terms in ' . $vocabulary1_id,
'edit terms in ' . $vocabulary2_id,
'delete terms in ' . $vocabulary2_id,
]);
$this->drupalLogin($edit_delete_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->linkExists('Edit');
$assert_session->linkExists('Delete');
$assert_session->linkNotExists('Add term');
$assert_session->buttonExists('Save');
$assert_session->pageTextContains('Weight');
$assert_session->fieldExists('Weight');
$assert_session->pageTextContains($edit_help_text);
$this->submitForm([], 'Reset to alphabetical');
$assert_session->statusCodeEquals(200);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('No terms available');
$assert_session->linkNotExists('Add term');
// Login as a user with permission to create new terms, only add new term
// link should be visible.
$edit_user = $this->createUser([
'access taxonomy overview',
'create terms in ' . $vocabulary1_id,
'create terms in ' . $vocabulary2_id,
]);
$this->drupalLogin($edit_user);
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary1_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->linkNotExists('Edit');
$assert_session->linkNotExists('Delete');
$assert_session->linkExists('Add term');
$assert_session->buttonNotExists('Save');
$assert_session->buttonNotExists('Reset to alphabetical');
$assert_session->pageTextContains('Weight');
$assert_session->fieldNotExists('Weight');
$assert_session->pageTextContains($no_edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary2_id . '/overview');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains('No terms available');
$assert_session->linkExists('Add term');
// Ensure the dynamic vocabulary permissions have the correct dependencies.
$permissions = \Drupal::service('user.permissions')->getPermissions();
$this->assertTrue(isset($permissions['create terms in ' . $vocabulary1_id]));
$this->assertEquals(['config' => [$vocabulary1->getConfigDependencyName()]], $permissions['create terms in ' . $vocabulary1_id]['dependencies']);
}
/**
* Create, edit and delete a taxonomy term via the user interface.
*/
public function testVocabularyPermissionsTaxonomyTerm(): void {
// Vocabulary used for creating, removing and editing terms.
$vocabulary = $this->createVocabulary();
// Test as admin user.
$user = $this->drupalCreateUser(['administer taxonomy']);
$this->drupalLogin($user);
// Visit the main taxonomy administration page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->fieldExists('edit-name-0-value');
// Submit the term.
$edit = [];
$edit['name[0][value]'] = $this->randomMachineName();
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Created new term ' . $edit['name[0][value]'] . '.');
// Verify that the creation message contains a link to a term.
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "term/")]');
$terms = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties(['name' => $edit['name[0][value]']]);
$term = reset($terms);
// Edit the term.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($edit['name[0][value]']);
$edit['name[0][value]'] = $this->randomMachineName();
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Updated term ' . $edit['name[0][value]'] . '.');
// Delete the vocabulary.
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
$this->assertSession()->pageTextContains("Are you sure you want to delete the taxonomy term {$edit['name[0][value]']}?");
// Confirm deletion.
$this->submitForm([], 'Delete');
$this->assertSession()->pageTextContains("Deleted term {$edit['name[0][value]']}.");
// Test as user with "create" permissions.
$user = $this->drupalCreateUser(["create terms in {$vocabulary->id()}"]);
$this->drupalLogin($user);
$assert_session = $this->assertSession();
// Create a new term.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
$assert_session->statusCodeEquals(200);
$assert_session->fieldExists('name[0][value]');
// Submit the term.
$edit = [];
$edit['name[0][value]'] = $this->randomMachineName();
$this->submitForm($edit, 'Save');
$assert_session->pageTextContains("Created new term {$edit['name[0][value]']}.");
$terms = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties(['name' => $edit['name[0][value]']]);
$term = reset($terms);
// Ensure that edit and delete access is denied.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$assert_session->statusCodeEquals(403);
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
$assert_session->statusCodeEquals(403);
// Test as user with "edit" permissions.
$user = $this->drupalCreateUser(["edit terms in {$vocabulary->id()}"]);
$this->drupalLogin($user);
// Ensure the taxonomy term add form is denied.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
$this->assertSession()->statusCodeEquals(403);
// Create a test term.
$term = $this->createTerm($vocabulary);
// Edit the term.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($term->getName());
$edit['name[0][value]'] = $this->randomMachineName();
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Updated term ' . $edit['name[0][value]'] . '.');
// Verify that the update message contains a link to a term.
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "term/")]');
// Ensure the term cannot be deleted.
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
$this->assertSession()->statusCodeEquals(403);
// Test as user with "delete" permissions.
$user = $this->drupalCreateUser(["delete terms in {$vocabulary->id()}"]);
$this->drupalLogin($user);
// Ensure the taxonomy term add form is denied.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
$this->assertSession()->statusCodeEquals(403);
// Create a test term.
$term = $this->createTerm($vocabulary);
// Ensure that the term cannot be edited.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->assertSession()->statusCodeEquals(403);
// Delete the vocabulary.
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
$this->assertSession()->pageTextContains("Are you sure you want to delete the taxonomy term {$term->getName()}?");
// Confirm deletion.
$this->submitForm([], 'Delete');
$this->assertSession()->pageTextContains("Deleted term {$term->getName()}.");
// Test as user without proper permissions.
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
// Ensure the taxonomy term add form is denied.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
$this->assertSession()->statusCodeEquals(403);
// Create a test term.
$term = $this->createTerm($vocabulary);
// Ensure that the term cannot be edited.
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
$this->assertSession()->statusCodeEquals(403);
// Ensure the term cannot be deleted.
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
$this->assertSession()->statusCodeEquals(403);
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\BrowserTestBase;
/**
* Regression test for https://www.drupal.org/node/2807263.
*
* When a Vocabulary entity is unserialized before the modules have been loaded
* (which happens in the KernelPreHandle Stack middleware), then the constants
* that the Vocabulary entity uses are not yet available because they are set in
* taxonomy.module. This means that for example the PageCache middleware cannot
* load any cached Vocabulary entity, because unserialization will fail.
*
* @group taxonomy
*/
class VocabularySerializationTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'vocabulary_serialization_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
Vocabulary::create(['vid' => 'test', 'name' => 'Test'])->save();
}
public function testSerialization(): void {
$this->drupalGet('/vocabulary_serialization_test/test');
$this->assertSession()->statusCodeEquals(200);
$this->assertSame('this is the output', $this->getSession()->getPage()->getContent());
$this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'MISS');
$this->drupalGet('/vocabulary_serialization_test/test');
$this->assertSession()->statusCodeEquals(200);
$this->assertSame('this is the output', $this->getSession()->getPage()->getContent());
$this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT');
}
}

View File

@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests content translation for vocabularies.
*
* @group taxonomy
*/
class VocabularyTranslationTest extends TaxonomyTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'content_translation',
'language',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Languages to enable.
*
* @var string[]
*/
protected $additionalLangcodes = ['es'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create an administrative user.
$this->drupalLogin($this->drupalCreateUser([
'administer taxonomy',
'administer content translation',
'translate configuration',
]));
// Add languages.
foreach ($this->additionalLangcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
}
/**
* Tests language settings for vocabularies.
*/
public function testVocabularyLanguage(): void {
$this->drupalGet('admin/structure/taxonomy/add');
// Check that the field to enable content translation is available.
$this->assertSession()->fieldExists('edit-default-language-content-translation');
// Create the vocabulary.
$vid = $this->randomMachineName();
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['langcode'] = 'en';
$edit['vid'] = $vid;
$edit['default_language[content_translation]'] = TRUE;
$this->submitForm($edit, 'Save');
// Check if content translation is enabled on the edit page.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
$this->assertSession()->checkboxChecked('edit-default-language-content-translation');
}
/**
* Tests vocabulary name translation for the overview and reset pages.
*/
public function testVocabularyTitleLabelTranslation(): void {
$this->drupalGet('admin/structure/taxonomy/add');
// Create the vocabulary.
$vid = $this->randomMachineName();
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['langcode'] = 'en';
$edit['vid'] = $vid;
$edit['default_language[content_translation]'] = TRUE;
$this->submitForm($edit, 'Save');
$langcode = $this->additionalLangcodes[0];
$vid_name = $edit['name'];
$translated_vid_name = "Translated $vid_name";
$this->assertSession()->pageTextContains($vid_name);
// Assert that the name label is displayed on the translation form with the
// right value.
$this->drupalGet("admin/structure/taxonomy/manage/$vid/translate/$langcode/add");
// Translate the name label.
$this->submitForm(["translation[config_names][taxonomy.vocabulary.$vid][name]" => $translated_vid_name], 'Save translation');
// Assert that the right name label is displayed on the taxonomy term
// overview page.
$this->drupalGet("admin/structure/taxonomy/manage/$vid/overview");
$this->assertSession()->pageTextContains($vid_name);
$this->drupalGet("$langcode/admin/structure/taxonomy/manage/$vid/overview");
$this->assertSession()->pageTextContains($translated_vid_name);
// Assert that the right name label is displayed on the taxonomy reset page.
$this->drupalGet("admin/structure/taxonomy/manage/$vid/reset");
$this->assertSession()->pageTextContains($vid_name);
$this->drupalGet("$langcode/admin/structure/taxonomy/manage/$vid/reset");
$this->assertSession()->pageTextContains($translated_vid_name);
}
}

View File

@@ -0,0 +1,189 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Url;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the taxonomy vocabulary interface.
*
* @group taxonomy
*/
class VocabularyUiTest extends TaxonomyTestBase {
/**
* The vocabulary used for creating terms.
*
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $vocabulary;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
$this->vocabulary = $this->createVocabulary();
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('page_title_block');
}
/**
* Create, edit and delete a vocabulary via the user interface.
*/
public function testVocabularyInterface(): void {
// Visit the main taxonomy administration page.
$this->drupalGet('admin/structure/taxonomy');
// Create a new vocabulary.
$this->clickLink('Add vocabulary');
$edit = [];
$vid = $this->randomMachineName();
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['vid'] = $vid;
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Created new vocabulary {$edit['name']}.");
// Edit the vocabulary.
$this->drupalGet('admin/structure/taxonomy');
$this->assertSession()->pageTextContains($edit['name']);
$this->assertSession()->pageTextContains($edit['description']);
$this->assertSession()->linkByHrefExists(Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $edit['vid']])->toString());
$this->clickLink('Edit vocabulary');
$edit = [];
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$this->submitForm($edit, 'Save');
$this->drupalGet('admin/structure/taxonomy');
$this->assertSession()->pageTextContains($edit['name']);
$this->assertSession()->pageTextContains($edit['description']);
// Try to submit a vocabulary with a duplicate machine name.
$edit['vid'] = $vid;
$this->drupalGet('admin/structure/taxonomy/add');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.');
// Try to submit an invalid machine name.
$edit['vid'] = '!&^%';
$this->drupalGet('admin/structure/taxonomy/add');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('The machine-readable name must contain only lowercase letters, numbers, and underscores.');
// Ensure that vocabulary titles are escaped properly.
$edit = [];
$edit['name'] = 'Don\'t Panic';
$edit['description'] = $this->randomMachineName();
$edit['vid'] = 'don_t_panic';
$this->drupalGet('admin/structure/taxonomy/add');
$this->submitForm($edit, 'Save');
$site_name = $this->config('system.site')->get('name');
$this->assertSession()->titleEquals("Don't Panic | $site_name");
// Delete the vocabulary.
$this->drupalGet('admin/structure/taxonomy');
$href = Url::fromRoute('entity.taxonomy_vocabulary.delete_form', ['taxonomy_vocabulary' => $edit['vid']])->toString();
$xpath = $this->assertSession()->buildXPathQuery('//a[contains(@href, :href)]', [':href' => $href]);
$link = $this->assertSession()->elementExists('xpath', $xpath);
$this->assertEquals('Delete vocabulary', $link->getText());
$link->click();
// Confirm deletion.
$this->assertSession()->responseContains(new FormattableMarkup('Are you sure you want to delete the vocabulary %name?', ['%name' => $edit['name']]));
$this->submitForm([], 'Delete');
$this->assertSession()->responseContains(new FormattableMarkup('Deleted vocabulary %name.', ['%name' => $edit['name']]));
$this->container->get('entity_type.manager')->getStorage('taxonomy_vocabulary')->resetCache();
$this->assertNull(Vocabulary::load($edit['vid']), 'Vocabulary not found.');
}
/**
* Changing weights on the vocabulary overview with two or more vocabularies.
*/
public function testTaxonomyAdminChangingWeights(): void {
// Create some vocabularies.
for ($i = 0; $i < 10; $i++) {
$this->createVocabulary();
}
// Get all vocabularies and change their weights.
$vocabularies = Vocabulary::loadMultiple();
$edit = [];
foreach ($vocabularies as $key => $vocabulary) {
$weight = -$vocabulary->get('weight');
$vocabularies[$key]->set('weight', $weight);
$edit['vocabularies[' . $key . '][weight]'] = $weight;
}
// Saving the new weights via the interface.
$this->drupalGet('admin/structure/taxonomy');
$this->submitForm($edit, 'Save');
// Load the vocabularies from the database.
$this->container->get('entity_type.manager')->getStorage('taxonomy_vocabulary')->resetCache();
$new_vocabularies = Vocabulary::loadMultiple();
// Check that the weights are saved in the database correctly.
foreach ($vocabularies as $key => $vocabulary) {
$this->assertEquals($new_vocabularies[$key]->get('weight'), $vocabularies[$key]->get('weight'), 'The vocabulary weight was changed.');
}
}
/**
* Tests the vocabulary overview with no vocabularies.
*/
public function testTaxonomyAdminNoVocabularies(): void {
// Delete all vocabularies.
$vocabularies = Vocabulary::loadMultiple();
foreach ($vocabularies as $key => $vocabulary) {
$vocabulary->delete();
}
// Confirm that no vocabularies are found in the database.
$this->assertEmpty(Vocabulary::loadMultiple(), 'No vocabularies found.');
$this->drupalGet('admin/structure/taxonomy');
// Check the default message for no vocabularies.
$this->assertSession()->pageTextContains('No vocabularies available.');
}
/**
* Deleting a vocabulary.
*/
public function testTaxonomyAdminDeletingVocabulary(): void {
// Create a vocabulary.
$vid = $this->randomMachineName();
$edit = [
'name' => $this->randomMachineName(),
'vid' => $vid,
];
$this->drupalGet('admin/structure/taxonomy/add');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('Created new vocabulary');
// Check the created vocabulary.
$this->container->get('entity_type.manager')->getStorage('taxonomy_vocabulary')->resetCache();
$vocabulary = Vocabulary::load($vid);
$this->assertNotEmpty($vocabulary, 'Vocabulary found.');
// Delete the vocabulary.
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id());
$this->clickLink('Delete');
$this->assertSession()->pageTextContains("Are you sure you want to delete the vocabulary {$vocabulary->label()}?");
$this->assertSession()->pageTextContains('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.');
// Confirm deletion.
$this->submitForm([], 'Delete');
$this->assertSession()->pageTextContains("Deleted vocabulary {$vocabulary->label()}.");
$this->container->get('entity_type.manager')->getStorage('taxonomy_vocabulary')->resetCache();
$this->assertNull(Vocabulary::load($vid), 'Vocabulary not found.');
}
}

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\ContextProvider;
use Drupal\Core\Routing\RouteMatch;
use Drupal\KernelTests\KernelTestBase;
use Drupal\taxonomy\ContextProvider\TermRouteContext;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* @coversDefaultClass \Drupal\taxonomy\ContextProvider\TermRouteContext
*
* @group taxonomy
*/
class TermContextTest extends KernelTestBase {
use TaxonomyTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['filter', 'taxonomy', 'text', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installConfig(['filter']);
$this->installEntitySchema('user');
$this->installEntitySchema('taxonomy_term');
}
/**
* @covers ::getAvailableContexts
*/
public function testGetAvailableContexts(): void {
$context_repository = $this->container->get('context.repository');
// Test taxonomy_term.taxonomy_term_route_context:taxonomy_term exists.
$contexts = $context_repository->getAvailableContexts();
$this->assertArrayHasKey('@taxonomy_term.taxonomy_term_route_context:taxonomy_term', $contexts);
$this->assertSame('entity:taxonomy_term', $contexts['@taxonomy_term.taxonomy_term_route_context:taxonomy_term']->getContextDefinition()
->getDataType());
}
/**
* @covers ::getRuntimeContexts
*/
public function testGetRuntimeContexts(): void {
// Create term.
$vocabulary = $this->createVocabulary();
$term = $this->createTerm($vocabulary);
// Create RouteMatch from term entity.
$url = $term->toUrl();
$route_provider = \Drupal::service('router.route_provider');
$route = $route_provider->getRouteByName($url->getRouteName());
$route_match = new RouteMatch($url->getRouteName(), $route, [
'taxonomy_term' => $term,
]);
// Initiate TermRouteContext with RouteMatch.
$provider = new TermRouteContext($route_match);
$runtime_contexts = $provider->getRuntimeContexts([]);
$this->assertArrayHasKey('taxonomy_term', $runtime_contexts);
$this->assertTrue($runtime_contexts['taxonomy_term']->hasContextValue());
}
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
/**
* Tests the loading of multiple taxonomy terms at once.
*
* @group taxonomy
*/
class LoadMultipleTest extends KernelTestBase {
use TaxonomyTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'taxonomy',
'user',
'text',
'filter',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('taxonomy_term');
}
/**
* Tests loading multiple taxonomy terms by term ID and vocabulary.
*/
public function testTaxonomyTermMultipleLoad(): void {
// Create a vocabulary.
$vocabulary = $this->createVocabulary();
// Create five terms in the vocabulary.
$i = 0;
while ($i < 5) {
$i++;
$this->createTerm($vocabulary);
}
// Load the terms from the vocabulary.
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $term_storage->loadByProperties(['vid' => $vocabulary->id()]);
$count = count($terms);
$this->assertEquals(5, $count, "Correct number of terms were loaded. $count terms.");
// Load the same terms again by tid.
$terms2 = Term::loadMultiple(array_keys($terms));
$this->assertEquals($terms, $terms2, 'Both arrays contain the same terms.');
// Remove one term from the array, then delete it.
$deleted = array_shift($terms2);
$deleted->delete();
$deleted_term = Term::load($deleted->id());
$this->assertNull($deleted_term);
// Load terms from the vocabulary by vid.
$terms3 = $term_storage->loadByProperties(['vid' => $vocabulary->id()]);
$this->assertCount(4, $terms3, 'Correct number of terms were loaded.');
$this->assertFalse(isset($terms3[$deleted->id()]));
// Create a single term and load it by name.
$term = $this->createTerm($vocabulary);
$loaded_terms = $term_storage->loadByProperties(['name' => $term->getName()]);
$this->assertCount(1, $loaded_terms, 'One term was loaded.');
$loaded_term = reset($loaded_terms);
$this->assertEquals($term->id(), $loaded_term->id(), 'Term loaded by name successfully.');
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\Migrate;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to taxonomy.settings.yml.
*
* @group migrate_drupal_6
*/
class MigrateTaxonomyConfigsTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigration('taxonomy_settings');
}
/**
* Tests migration of taxonomy variables to taxonomy.settings.yml.
*/
public function testTaxonomySettings(): void {
$config = $this->config('taxonomy.settings');
$this->assertSame(100, $config->get('terms_per_page_admin'));
$this->assertFalse($config->get('override_selector'));
$this->assertTrue($config->get('maintain_index_table'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'taxonomy.settings', $config->get());
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\Migrate;
use Drupal\migrate\MigrateExecutable;
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
use Drupal\migrate_drupal\Tests\StubTestTrait;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Test stub creation for taxonomy terms.
*
* @group taxonomy
*/
class MigrateTaxonomyTermStubTest extends MigrateDrupalTestBase {
use StubTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy', 'text', 'taxonomy_term_stub_test'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('taxonomy_term');
}
/**
* Tests creation of taxonomy term stubs.
*/
public function testStub(): void {
Vocabulary::create([
'vid' => 'test_vocabulary',
'name' => 'Test vocabulary',
])->save();
$this->performStubTest('taxonomy_term');
}
/**
* Tests creation of stubs when weight is mapped.
*/
public function testStubWithWeightMapping(): void {
// Create a vocabulary via migration for the terms to reference.
$vocabulary_data_rows = [
['id' => '1', 'name' => 'tags'],
];
$ids = ['id' => ['type' => 'integer']];
$definition = [
'migration_tags' => ['Stub test'],
'source' => [
'plugin' => 'embedded_data',
'data_rows' => $vocabulary_data_rows,
'ids' => $ids,
],
'process' => [
'vid' => 'id',
'name' => 'name',
],
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
];
$vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
$vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
$vocabulary_executable->import();
// We have a term referencing an unmigrated parent, forcing a stub to be
// created.
$migration = $this->getMigration('taxonomy_term_stub_test');
$term_executable = new MigrateExecutable($migration, $this);
$term_executable->import();
$this->assertNotEmpty($migration->getIdMap()->getRowBySource(['2']), 'Stub row exists in the ID map table');
// Load the referenced term, which should exist as a stub.
/** @var \Drupal\Core\Entity\ContentEntityBase $stub_entity */
$stub_entity = Term::load(2);
$this->assertNotEmpty($stub_entity, 'Stub successfully created');
if ($stub_entity) {
$this->assertCount(0, $stub_entity->validate(), 'Stub is a valid entity');
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\Migrate;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
// cspell:ignore vocabfixed vocablocalized vocabtranslate
/**
* Tests d7 taxonomy term deriver.
*
* @group migrate_drupal_7
*/
class TaxonomyTermDeriverTest extends MigrateDrupal7TestBase {
protected static $modules = ['taxonomy', 'text'];
/**
* Tests fields exist in process pipeline for term migrations.
*/
public function testBuilder(): void {
// Test a field on the vocabfixed term.
$process = $this->getMigration('d7_taxonomy_term:vocabfixed')->getProcess();
$this->assertSame('field_training', $process['field_training'][0]['source']);
// Test a field on the vocablocalized term.
$process = $this->getMigration('d7_taxonomy_term:vocablocalized')->getProcess();
$this->assertSame('field_sector', $process['field_sector'][0]['source']);
// Test a field on the vocabtranslate term.
$process = $this->getMigration('d7_taxonomy_term:vocabtranslate')->getProcess();
$this->assertSame('field_chancellor', $process['field_chancellor'][0]['source']);
// Test a field on the test_vocabulary term.
$process = $this->getMigration('d7_taxonomy_term:test_vocabulary')->getProcess();
$this->assertSame('field_integer', $process['field_integer'][0]['source']);
$this->assertSame('field_term_reference', $process['field_term_reference'][0]['source']);
}
}

View File

@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\Migrate\d6;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade taxonomy terms.
*
* @group migrate_drupal_6
*/
class MigrateTaxonomyTermTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['comment', 'taxonomy'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('taxonomy_term');
$this->executeMigrations(['d6_taxonomy_vocabulary', 'd6_taxonomy_term']);
}
/**
* Tests the Drupal 6 taxonomy term to Drupal 8 migration.
*/
public function testTaxonomyTerms(): void {
$expected_results = [
'1' => [
'source_vid' => 1,
'vid' => 'vocabulary_1_i_0_',
'weight' => 0,
'parent' => [0],
'language' => 'zu',
],
'2' => [
'source_vid' => 2,
'vid' => 'vocabulary_2_i_1_',
'weight' => 3,
'parent' => [0],
'language' => 'fr',
],
'3' => [
'source_vid' => 2,
'vid' => 'vocabulary_2_i_1_',
'weight' => 4,
'parent' => [2],
'language' => 'fr',
],
'4' => [
'source_vid' => 3,
'vid' => 'vocabulary_3_i_2_',
'weight' => 6,
'parent' => [0],
],
'5' => [
'source_vid' => 3,
'vid' => 'vocabulary_3_i_2_',
'weight' => 7,
'parent' => [4],
],
'6' => [
'source_vid' => 3,
'vid' => 'vocabulary_3_i_2_',
'weight' => 8,
'parent' => [4, 5],
],
];
$terms = Term::loadMultiple(array_keys($expected_results));
// Find each term in the tree.
$storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$vids = array_unique(array_column($expected_results, 'vid'));
$tree_terms = [];
foreach ($vids as $vid) {
foreach ($storage->loadTree($vid) as $term) {
$tree_terms[$term->tid] = $term;
}
}
foreach ($expected_results as $tid => $values) {
/** @var \Drupal\taxonomy\Entity\Term $term */
$term = $terms[$tid];
$language = isset($values['language']) ? $values['language'] . ' - ' : '';
$this->assertSame("{$language}term {$tid} of vocabulary {$values['source_vid']}", $term->name->value);
$this->assertSame("{$language}description of term {$tid} of vocabulary {$values['source_vid']}", $term->description->value);
$this->assertSame($values['vid'], $term->vid->target_id);
$this->assertSame((string) $values['weight'], $term->weight->value);
if ($values['parent'] === [0]) {
$this->assertSame(0, (int) $term->parent->target_id);
}
else {
$parents = [];
foreach (\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadParents($tid) as $parent) {
$parents[] = (int) $parent->id();
}
$this->assertSame($parents, $values['parent']);
}
$this->assertArrayHasKey($tid, $tree_terms, "Term $tid exists in vocabulary tree");
$tree_term = $tree_terms[$tid];
// PostgreSQL, MySQL and SQLite may not return the parent terms in the
// same order so sort before testing.
$expected_parents = $values['parent'];
sort($expected_parents);
$actual_parents = $tree_term->parents;
sort($actual_parents);
$this->assertEquals($expected_parents, $actual_parents, "Term $tid has correct parents in vocabulary tree");
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\Migrate\d6;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Migrate taxonomy vocabularies to taxonomy.vocabulary.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateTaxonomyVocabularyTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigration('d6_taxonomy_vocabulary');
}
/**
* Tests the Drupal 6 taxonomy vocabularies to Drupal 8 migration.
*/
public function testTaxonomyVocabulary(): void {
for ($i = 0; $i < 3; $i++) {
$j = $i + 1;
$vocabulary = Vocabulary::load("vocabulary_{$j}_i_{$i}_");
$this->assertSame($this->getMigration('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationIds([$j]), [[$vocabulary->id()]]);
$this->assertSame("vocabulary $j (i=$i)", $vocabulary->label());
$this->assertSame("description of vocabulary $j (i=$i)", $vocabulary->getDescription());
$this->assertSame(4 + $i, $vocabulary->get('weight'));
}
$vocabulary = Vocabulary::load('vocabulary_name_much_longer_th');
$this->assertSame('vocabulary name much longer than thirty two characters', $vocabulary->label());
$this->assertSame('description of vocabulary name much longer than thirty two characters', $vocabulary->getDescription());
$this->assertSame(7, $vocabulary->get('weight'));
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Migrate taxonomy vocabularies to taxonomy.vocabulary.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateTaxonomyVocabularyTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_translation',
'language',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigrations([
'language',
'd6_taxonomy_vocabulary',
'd6_taxonomy_vocabulary_translation',
]);
}
/**
* Tests the Drupal 6 i18n taxonomy vocabularies to Drupal 8 migration.
*/
public function testTaxonomyVocabularyTranslation(): void {
$language_manager = \Drupal::service('language_manager');
$config = $language_manager->getLanguageConfigOverride('zu', 'taxonomy.vocabulary.vocabulary_1_i_0_');
$this->assertSame('zu - vocabulary 1 (i=0)', $config->get('name'));
$config = $language_manager->getLanguageConfigOverride('fr', 'taxonomy.vocabulary.vocabulary_1_i_0_');
$this->assertSame('fr - vocabulary 1 (i=0)', $config->get('name'));
$config = $language_manager->getLanguageConfigOverride('fr', 'taxonomy.vocabulary.vocabulary_2_i_1_');
$this->assertSame('fr - vocabulary 2 (i=1)', $config->get('name'));
$config = $language_manager->getLanguageConfigOverride('fr', 'taxonomy.vocabulary.vocabulary_3_i_2_');
$this->assertSame('fr - vocabulary 3 (i=2)', $config->get('name'));
$config = $language_manager->getLanguageConfigOverride('fr', 'taxonomy.vocabulary.vocabulary_name_much_longer_th');
$this->assertSame('Nom de vocabulaire beaucoup plus long que trente-deux caractères', $config->get('name'));
$config = $language_manager->getLanguageConfigOverride('fr', 'taxonomy.vocabulary.tags');
$this->assertSame('fr - Tags', $config->get('name'));
}
}

Some files were not shown because too many files have changed in this diff Show More