added simple math and charts modules

This commit is contained in:
2024-12-10 23:53:23 +01:00
parent 62a5f3c97c
commit 688ff42752
196 changed files with 20285 additions and 8 deletions

View File

@@ -0,0 +1,12 @@
name: Charts test
type: module
description: 'Provides functionality for testing the charts module.'
package: Testing
core_version_requirement: ^8.8 || ^9 || ^10 || ^11
dependencies:
- charts:charts
# Information added by Drupal.org packaging script on 2024-12-02
version: '5.1.1'
project: 'charts'
datestamp: 1733170633

View File

@@ -0,0 +1,8 @@
charts_test.data_collector_table_test_form:
path: '/charts_test/data_collector_table_test_form'
defaults:
_form: '\Drupal\charts_test\Form\DataCollectorTableTestForm'
_title: 'Data collector table test form'
requirements:
# Needs to be accessed by an anonymous user.
_access: 'TRUE'

View File

@@ -0,0 +1,6 @@
charts.settings.library_plugin.charts_test_library:
type: mapping
mapping:
foo:
label: Foo
type: string

View File

@@ -0,0 +1,52 @@
<?php
namespace Drupal\charts_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* The data collector table test form to test the DataCollectorTable element.
*
* @package Drupal\charts_test\Form
*/
class DataCollectorTableTestForm extends FormBase {
const INITIAL_ROWS = 3;
const INITIAL_COLUMNS = 2;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'charts_data_collector_table_test_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('charts.settings');
$form['series'] = [
'#type' => 'chart_data_collector_table',
'#initial_rows' => self::INITIAL_ROWS,
'#initial_columns' => self::INITIAL_COLUMNS,
'#table_drag' => FALSE,
'#default_colors' => $config->get('charts_default_settings.display.colors') ?? [],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// {"name":"Number of players","color":"#0d233a","data":
// [50,60,100,132,133,234]},{"name":"Number of coaches","color":.
// "#ff0000","data":
// [50,80,100,32,133,234]}.
// A,b,c,d,e,f
}
}

View File

@@ -0,0 +1,166 @@
<?php
namespace Drupal\charts_test\Plugin\chart\Library;
use Drupal\charts\Plugin\chart\Library\ChartBase;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\ElementInfoManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Define a library to use for testing purposes.
*
* @Chart(
* id = "charts_test_library",
* name = @Translation("Charts Test Library"),
* types = {
* "area",
* "bar",
* "bubble",
* "column",
* "donut",
* "gauge",
* "line",
* "pie",
* "scatter",
* },
* )
*/
class ChartsTestLibrary extends ChartBase implements ContainerFactoryPluginInterface {
/**
* The element info manager.
*
* @var \Drupal\Core\Render\ElementInfoManagerInterface
*/
protected $elementInfo;
/**
* Constructs a \Drupal\views\Plugin\Block\ViewsBlockBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
* The element info manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ElementInfoManagerInterface $element_info) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->elementInfo = $element_info;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('element_info')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'foo' => 'bar',
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function preRender(array $element) {
// Populate chart settings.
$chart_definition = [];
$chart_definition = $this->populateOptions($element, $chart_definition);
$chart_definition = $this->populateData($element, $chart_definition);
if (!empty($element['#height']) || !empty($element['#width'])) {
$element['#attributes']['style'] = 'height:' . $element['#height'] . $element['#height_units'] . ';width:' . $element['#width'] . $element['#width_units'] . ';';
}
if (!isset($element['#id'])) {
$element['#id'] = Html::getUniqueId('charts-charts-test-library-chart');
}
$element['#attributes']['class'][] = 'charts-charts-test-library-chart-container';
$element['#chart_definition'] = $chart_definition;
return $element;
}
/**
* Populate options.
*
* @param array $element
* The element.
* @param array $chart_definition
* The chart definition.
*
* @return array
* Return the chart definition.
*/
private function populateOptions(array $element, array $chart_definition) {
$chart_definition['title']['text'] = $element['#title'];
$chart_definition['title']['color'] = $element['#title_color'];
$chart_definition['title']['position'] = $element['#title_position'];
$chart_definition['subtitle']['text'] = $element['#subtitle'];
$chart_definition['type'] = $element['#chart_type'];
$chart_definition['title']['font'] = [
'weight' => $element['#title_font_weight'],
'style' => $element['#title_font_style'],
'size' => $element['#title_font_size'],
];
$chart_definition['colors'] = $element['#colors'];
$chart_definition['tooltips'] = $element['#tooltips'];
$chart_definition['foo_configuration'] = $this->configuration['foo'] ?? '';
// Merge in chart raw options.
if (!empty($element['#raw_options'])) {
$chart_definition = NestedArray::mergeDeep($chart_definition, $element['#raw_options']);
}
return $chart_definition;
}
/**
* Populate data.
*
* @param array $element
* The element.
* @param array $chart_definition
* The chart definition.
*
* @return array
* Return the chart definition.
*/
private function populateData(array &$element, array $chart_definition) {
$chart_definition['series'] = [];
foreach (Element::children($element) as $key) {
$type = $element[$key]['#type'];
if ($type !== 'chart_data') {
continue;
}
$chart_definition['series'][] = [
'name' => $element[$key]['#title'],
'color' => $element[$key]['#color'],
'value' => $element[$key]['#data'],
];
}
return $chart_definition;
}
}

View File

@@ -0,0 +1,144 @@
langcode: en
status: true
dependencies: { }
id: test_charts
label: ''
module: views
description: ''
tag: ''
base_table: views_test_data
base_field: nid
display:
default:
id: default
display_title: Default
display_plugin: default
position: 0
display_options:
title: 'Test charts style'
defaults:
fields: false
pager: false
sorts: false
fields:
age:
id: age
field: age
relationship: none
table: views_test_data
plugin_id: numeric
id:
id: id
field: id
relationship: none
table: views_test_data
plugin_id: numeric
name:
id: name
field: name
relationship: none
table: views_test_data
plugin_id: string
job:
id: job
field: job
relationship: none
table: views_test_data
plugin_id: string
pager:
type: none
options:
offset: 0
empty:
area_text_custom:
id: area_text_custom
table: views
field: area_text_custom
relationship: none
group_type: group
admin_label: ''
plugin_id: text_custom
empty: true
content: 'custom text for charts'
tokenize: false
sorts:
id:
field: id
id: id
order: ASC
relationship: none
table: views_test_data
plugin_id: numeric
style:
type: chart
options:
grouping: { }
chart_settings:
library: charts_test_library
type: column
fields:
label: name
stacking: false
data_providers:
age:
enabled: true
color: '#ff9300'
weight: 4
id:
enabled: false
color: '#72fcd5'
weight: 4
name:
enabled: false
color: '#d783ff'
weight: 4
job:
enabled: false
color: '#b76b1f'
weight: 4
display:
title: 'Test charts'
title_position: out
subtitle: ''
data_labels: true
data_markers: true
legend_position: bottom
background: ''
three_dimensional: 0
polar: 0
tooltips: true
dimensions:
width: ''
width_units: ''
height: ''
height_units: ''
gauge:
max: ''
min: ''
green_from: ''
green_to: ''
yellow_from: ''
yellow_to: ''
red_from: ''
red_to: ''
color_changer: false
xaxis:
title: Date
labels_rotation: '0'
yaxis:
title: Downloads
min: ''
max: ''
prefix: ''
suffix: ''
decimal_count: ''
labels_rotation: '0'
row:
type: fields
page_1:
id: page_1
display_title: 'Page display'
display_plugin: page
position: 1
display_options:
path: test-charts

View File

@@ -0,0 +1,7 @@
Categories,Number of players,Number of coaches
a,50,50
b,60,80
c,100,100
d,132,32
e,133,133
f,234,234
1 Categories Number of players Number of coaches
2 a 50 50
3 b 60 80
4 c 100 100
5 d 132 32
6 e 133 133
7 f 234 234

View File

@@ -0,0 +1,2 @@
Categories,a,b
Label,200,400
1 Categories a b
2 Label 200 400

View File

@@ -0,0 +1,59 @@
<?php
namespace Drupal\Tests\charts\Functional\Plugin;
use Drupal\Tests\views\Functional\ViewTestBase;
use Symfony\Component\HttpFoundation\Response;
/**
* Tests the chart style views plugin.
*
* @group charts
*/
class StyleChartsTest extends ViewTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_charts'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['charts', 'charts_test', 'views'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = ['charts_test']): void {
parent::setUp($import_test_views, $modules);
$this->enableViewsTestModule();
}
/**
* Tests the generated JSON generated from the views.
*/
public function testGeneratedJson() {
$this->drupalGet('test-charts');
$this->assertSession()->statusCodeEquals(Response::HTTP_OK);
$attribute = 'data-chart';
$chart_container = $this->assertSession()->elementAttributeExists('css', '#chart-test-charts-page-1', $attribute);
$actual = str_replace('&quot;', '"', (string) $chart_container->getAttribute($attribute));
$expected = '{"title":{"text":"Test charts","color":"#000","position":"out","font":{"weight":"normal","style":"normal","size":14}},"subtitle":{"text":""},"type":"column","colors":["#006fb0","#f07c33","#342e9c","#579b17","#3f067a","#cbde67","#7643b6","#738d00","#c157c7","#02dab1","#ed56b4","#d8d981","#004695","#736000","#a5a5ff","#833a00","#ff9ee9","#684507","#fe4f85","#5d0011","#ffa67b","#88005c","#ff9b8f","#85000f","#ff7581"],"tooltips":true,"foo_configuration":"","series":[]}';
$this->assertEquals($expected, $actual);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Drupal\Tests\charts\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\charts\Traits\ConfigUpdateTrait;
/**
* Tests that chart configuration can be overridden.
*
* @group charts
*/
class ConfigOverrideTest extends WebDriverTestBase {
use ConfigUpdateTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $modules = [
'charts',
'charts_chartjs',
];
/**
* Tests that charts config can be overridden.
*/
public function testOverridingConfig() {
$this->drupalLogin($this->rootUser);
// Set up an override of the cdn.
$settings['config']['charts.settings']['advanced']['requirements']['cdn'] = (object) [
'value' => FALSE,
'required' => TRUE,
];
$this->writeSettings($settings);
// Check the error message.
$this->drupalGet('admin/reports/status');
$this->assertSession()->pageTextContains('You are missing the Chart.js library in your Drupal installation directory and you have opted not to use a CDN.');
}
}

View File

@@ -0,0 +1,289 @@
<?php
namespace Drupal\Tests\charts\FunctionalJavascript;
use Drupal\charts_test\Form\DataCollectorTableTestForm;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\charts\Traits\ConfigUpdateTrait;
/**
* Tests the data collector table element.
*
* @group charts
*/
class DataCollectorTableTest extends WebDriverTestBase {
use ConfigUpdateTrait;
use StringTranslationTrait;
/**
* Default theme.
*
* @var string
*/
protected $defaultTheme = 'stark';
/**
* List modules.
*
* @var array
*/
protected static $modules = [
'charts',
'charts_test',
];
const TABLE_SELECTOR = 'table[data-drupal-selector="edit-series-data-collector-table"]';
const TABLE_ROW_SELECTOR = 'table[data-drupal-selector="edit-series-data-collector-table"] tr.data-collector-table--row';
const TABLE_COLUMN_SELECTOR = 'table[data-drupal-selector="edit-series-data-collector-table"] tbody tr:nth-child(1) td:not(.data-collector-table--row--delete)';
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->updateFooConfiguration('bar');
}
/**
* Tests the data collector table.
*/
public function testDataCollectorTable() {
$this->drupalGet('/charts_test/data_collector_table_test_form');
$table_id_selector = static::TABLE_SELECTOR;
// Count generated rows.
$rows = $this->cssSelect(static::TABLE_ROW_SELECTOR);
$this->assertTrue(count($rows) === DataCollectorTableTestForm::INITIAL_ROWS, 'Expected rows were found.');
// Count generated columns.
// Only using the first row to count columns.
$columns = $this->cssSelect(static::TABLE_COLUMN_SELECTOR);
$this->assertTrue(count($columns) === DataCollectorTableTestForm::INITIAL_COLUMNS, 'Expected columns were found.');
// Fill the table.
$cell_input_selector = $table_id_selector . ' tr.data-collector-table--row td > .data-collector-table--row--cell .form-text';
$cell_inputs = $this->cssSelect($cell_input_selector);
$this->assertNotEmpty($cell_inputs, $this->t('@count inputs were found', [
'@count' => count($cell_inputs),
]));
$this->fillInputs($cell_inputs);
// Test adding a row.
$this->doTableOperation('add', 'row');
// The New row should not have any data.
$this->assertNewCellIsEmpty('row');
// Test adding a column.
$this->doTableOperation('add', 'column');
// The New column should not have any data.
$this->assertNewCellIsEmpty('column');
// Delete the second and third row.
$this->doTableOperation('delete', 'row', [2, 3]);
// Remove the middle column.
$this->doTableOperation('delete', 'column', [2]);
// Import csv from resources.
// Opening the dom "details" element.
$series_import_details = $this->getSession()->getPage()->find('css', 'details[data-drupal-selector="edit-series-import"]');
$this->assertFalse($series_import_details->hasAttribute('open'), 'Details closed');
$this->getSession()->executeScript("document.querySelector('details[data-drupal-selector=edit-series-import]').setAttribute('open', true);");
$this->assertTrue($series_import_details->hasAttribute('open'), 'Details open');
// Add the CSV file containing the test data.
$file_field_name = 'files[series]';
$filename = $this->getResourcePath() . '/csv/first_column.csv';
$this->getSession()->getPage()->attachFileToField($file_field_name, $filename);
// Submit upload the file and wait for ajax processing.
$button_selector = 'details[data-drupal-selector="edit-series-import"] input[value="Upload CSV"]';
$this->pressAjaxButton($button_selector);
$this->assertSession()->assertWaitOnAjaxRequest(20000);
// Verify the uploaded data.
$page = $this->getSession()->getPage();
$targets = $page->findAll('css', static::TABLE_ROW_SELECTOR);
$this->assertEquals(7, count($targets), 'The count of number of expected rows match.');
$targets = $page->findAll('css', static::TABLE_COLUMN_SELECTOR);
$this->assertEquals(3, count($targets), 'The count of number of expected columns match.');
$cell_input = $page->find('css', 'input[name="series[data_collector_table][0][0][data]"]');
$this->assertEquals('Categories', $cell_input->getValue(), 'The cell value of row 1 at column 1 match.');
$cell_input = $page->find('css', 'input[name="series[data_collector_table][6][2][data]"]');
$this->assertEquals(234, $cell_input->getValue(), 'The last uploaded value in the cell match.');
}
/**
* Tests the default colors pn the "chart_data_collector_table" element.
*/
public function testColorDefaultColors() {
$chart_config = \Drupal::config('charts.settings');
$default_colors = $chart_config->get('charts_default_settings.display.colors');
$this->drupalGet('/charts_test/data_collector_table_test_form');
$page = $this->getSession()->getPage();
// Get the first row, then inside the first row get the color input and
// check its value.
$first_row_color_input = $page->find('css', static::TABLE_ROW_SELECTOR . ':first-child td:nth-child(2) input[type="color"]');
$this->assertEquals($default_colors[0], $first_row_color_input->getValue());
// Adding one column.
$this->doTableOperation('add', 'column');
// Checking if the added column also has the expected color.
$first_row_color_input = $page->find('css', static::TABLE_ROW_SELECTOR . ':first-child td:nth-child(3) input[type="color"]');
$this->assertEquals($default_colors[1], $first_row_color_input->getValue());
}
/**
* Do table operation.
*
* @param string $operation
* The operation.
* @param string $on
* The element.
* @param array $positions
* The position.
*/
protected function doTableOperation(string $operation, string $on, array $positions = []) {
$value = ucfirst($operation) . ' ' . $on;
if ($operation === 'add') {
$selector = static::TABLE_SELECTOR . ' input[value="' . $value . '"]';
$this->pressAjaxButton($selector);
if ($on === 'row') {
$this->assertRowsIncreased();
}
else {
$this->assertColumnsIncreased();
}
}
else {
$on_row = $on === 'row';
if ($on_row) {
$counter = DataCollectorTableTestForm::INITIAL_ROWS + 1;
$locator = static::TABLE_ROW_SELECTOR;
}
else {
$counter = DataCollectorTableTestForm::INITIAL_COLUMNS + 1;
$locator = static::TABLE_COLUMN_SELECTOR;
}
foreach ($positions as $position) {
if ($on_row) {
$button_selector = static::TABLE_ROW_SELECTOR . ':nth-child(' . $position . ') .data-collector-table--row--delete input[value="Delete row"]';
}
else {
$button_selector = static::TABLE_SELECTOR . ' .data-collector-table--column-deletes-row';
$button_selector .= ' .data-collector-table--column--delete:nth-child(' . $position . ') input[value="Delete column"]';
}
$this->pressAjaxButton($button_selector);
$this->assertDeletionOperation($counter, $locator);
}
}
}
/**
* Assert rows.
*/
protected function assertRowsIncreased() {
$page = $this->getSession()->getPage();
$this->assertTrue($page->waitFor(10, function ($page) {
$expected_rows = DataCollectorTableTestForm::INITIAL_ROWS + 1;
$rows = $page->findAll('css', static::TABLE_ROW_SELECTOR);
return count($rows) === $expected_rows;
}), 'Expected rows were increased by one after add row click.');
}
/**
* Assert columns.
*/
protected function assertColumnsIncreased() {
$page = $this->getSession()->getPage();
$this->assertTrue($page->waitFor(10, function ($page) {
$expected_rows = DataCollectorTableTestForm::INITIAL_COLUMNS + 1;
$columns = $page->findAll('css', static::TABLE_COLUMN_SELECTOR);
return count($columns) === $expected_rows;
}), 'Expected columns were increased by one after add column click.');
}
/**
* Assert new cell.
*
* @param string $on
* The element.
*/
protected function assertNewCellIsEmpty(string $on) {
$page = $this->getSession()->getPage();
if ($on === 'row') {
$counter = DataCollectorTableTestForm::INITIAL_ROWS + 1;
$selector = static::TABLE_ROW_SELECTOR . ':nth-child(' . $counter . ') td:first-child input';
}
else {
$counter = DataCollectorTableTestForm::INITIAL_COLUMNS + 1;
$selector = static::TABLE_COLUMN_SELECTOR . ':nth-child(' . $counter . ') input';
}
$cell_input = $page->find('css', $selector);
$this->assertEmpty($cell_input->getValue(), 'Added row cells are empty.');
}
/**
* Assert Deletion operation.
*
* @param int $current_count
* Current count.
* @param string $locator
* The locator.
*/
protected function assertDeletionOperation(int &$current_count, string $locator) {
$web_assert = $this->assertSession();
$web_assert->assertWaitOnAjaxRequest();
$current_count--;
$page = $this->getSession()->getPage();
$targets = $page->findAll('css', $locator);
$this->assertTrue(count($targets) === $current_count);
}
/**
* Press the ajax button.
*
* @param string $selector
* The selector.
*/
protected function pressAjaxButton(string $selector) {
$button = $this->assertSession()->waitForElementVisible('css', $selector);
$this->assertNotEmpty($button);
$button->click();
}
/**
* Fill inputs.
*
* @param array $inputs
* Input to fill.
*
* @return \Behat\Mink\Element\NodeElement[]
* The node element.
*/
protected function fillInputs(array $inputs) {
/** @var \Behat\Mink\Element\NodeElement[] $inputs */
foreach ($inputs as $input) {
$value = rand(0, count($inputs));
$input->setValue((string) $value);
}
return $inputs;
}
/**
* Get the path of the resource.
*
* @return string
* The resource folder path.
*/
protected function getResourcePath() {
return \Drupal::root() . '/' . \Drupal::service('extension.list.module')->getPath('charts') . '/tests/resources';
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Drupal\Tests\charts\Kernel;
use Drupal\Tests\charts\Traits\ConfigUpdateTrait;
/**
* Tests the chart type element.
*
* @group charts
*/
class ChartElementTest extends ChartsKernelTestBase {
use ConfigUpdateTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'charts',
'charts_test',
];
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->updateFooConfiguration('bar');
}
/**
* Tests the chart test element.
*/
public function testChartElement() {
$element = [
'#type' => 'chart',
'#library' => 'charts_test_library',
'#chart_type' => 'column',
];
$json = "{'title':{'text':null,'color':'#000','position':'out','font':{'weight':'normal','style':'normal','size':14}},'subtitle':{'text':null},'type':'column','colors':['#2f7ed8','#0d233a','#8bbc21','#910000','#1aadce','#492970','#f28f43','#77a1e5','#c42525','#a6c96a'],'tooltips':true,'foo_configuration':'bar','series':[]}";
$this->assertElementJson($element, $json);
// Testing raw options.
$element['#raw_options'] = ['title' => ['text' => 'Foo']];
$path = ['title', 'text'];
$this->assertJsonPropertyHasValue($element, $path, 'Foo');
$this->assertJsonPropertyHasValue($element, ['type'], 'column');
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Drupal\Tests\charts\Kernel;
use Drupal\Tests\charts\Traits\ConfigUpdateTrait;
/**
* Tests the chart library configurations that are part of default config.
*
* @group charts
*/
class ChartLibraryConfigTest extends ChartsKernelTestBase {
use ConfigUpdateTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'charts',
'charts_test',
];
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->updateFooConfiguration('bar');
}
/**
* Tests if library config is available after rendering.
*/
public function testLibraryConfigIsAvailable() {
$element = [
'#type' => 'chart',
'#library' => 'charts_test_library',
'#chart_type' => 'column',
];
$path = ['foo_configuration'];
// Confirm that 'foo_configuration' option is set to 'bar'.
$this->assertJsonPropertyHasValue($element, $path, 'bar');
// Check if we change the default config for the library config it will
// pick up after rendering.
$new_value = $this->randomString();
$this->updateFooConfiguration($new_value);
$this->assertJsonPropertyHasValue($element, $path, $new_value);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Drupal\Tests\charts\Kernel;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Tests\charts\Traits\ConfigUpdateTrait;
/**
* Tests the chart types support functionality.
*
* @group charts
*/
class ChartTypeSupportTest extends ChartsKernelTestBase {
use ConfigUpdateTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'charts',
'charts_test',
];
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->updateFooConfiguration('bar');
}
/**
* Tests the chart type support functionality.
*
* @param string $chart_type
* The chart type to test.
* @param string $expected_exception
* The expected exception.
* @param string $expected_exception_message
* The expected exception message.
*
* @dataProvider provideChartTypesData
*/
public function testNotSupportedChartType(string $chart_type, string $expected_exception, string $expected_exception_message) {
$element = [
'#type' => 'chart',
'#library' => 'charts_test_library',
'#chart_type' => $chart_type,
];
$this->expectException($expected_exception);
$this->expectExceptionMessage($expected_exception_message);
$this->renderer->renderRoot($element);
}
/**
* Provides data for chart types support test.
*
* @return array[]
* The chart types test cases.
*/
public function provideChartTypesData(): array {
return [
// Asserting that a chart type that is not a valid plugin chart type will
// throw a plugin not found exception during discovery.
'chart type plugin does not exist' => [
'not_supported',
PluginNotFoundException::class,
'The "not_supported" plugin does not exist. Valid plugin IDs for Drupal\charts\TypeManager are: area, bar, bubble, column, donut, gauge, line, pie, scatter, spline',
],
// Asserting that a chart type not supported by a chart library plugin
// will throw a logic exception.
'chart type plugin exists but not supported by the chart library plugin' => [
'spline',
\LogicException::class,
'The provided chart type "spline" is not supported by "Charts Test Library" chart plugin library.',
],
];
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Drupal\Tests\charts\Kernel;
use Drupal\Component\Utility\NestedArray;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\DomCrawler\Crawler;
/**
* Base class for chart kernel tests.
*/
abstract class ChartsKernelTestBase extends KernelTestBase {
/**
* The renderer service.
*
* @var \Drupal\Core\Render\Renderer
*/
protected $renderer;
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->renderer = $this->container->get('renderer');
}
/**
* Asserts that the given element returned the expected json.
*
* @param array $element
* The element to check.
* @param string $expected_json
* The expected json.
*/
public function assertElementJson(array $element, string $expected_json) {
$this->assertEquals($expected_json, $this->getDataChartJson($element));
}
/**
* Asserts that json property has value by given element.
*
* @param array $element
* The element structure.
* @param array $parents
* The parent structure where the value is.
* @param string $expected_value
* The expected value.
*/
public function assertJsonPropertyHasValue(array $element, array $parents, string $expected_value) {
$json = $this->getDataChartJson($element);
$decoded_json = json_decode(str_replace("'", '"', $json), TRUE);
$actual_value = NestedArray::getValue($decoded_json, $parents);
$this->assertEquals($expected_value, $actual_value);
}
/**
* Get the json data.
*
* @param array $element
* The element to be rendered.
*
* @return string
* The string with the data from the chart.
*/
private function getDataChartJson(array $element): string {
$renderer_output = $this->renderer->renderRoot($element);
$crawler = new Crawler();
$crawler->addHtmlContent(str_replace('&quot;', "'", $renderer_output));
return $crawler->filter('[data-chart]')->attr('data-chart');
}
/**
* Get the chart style from the element.
*
* @param array $element
* The element to be tested.
* @param string $filter_selector
* The selector to use for crawler filter.
*
* @return array
* The styles as an array.
*/
protected function getChartStyle(array $element, string $filter_selector = '[data-chart]'): array {
$renderer_output = $this->renderer->renderRoot($element);
$crawler = new Crawler();
$crawler->addHtmlContent(str_replace('&quot;', "'", $renderer_output));
if (!$crawler->filter($filter_selector)->count()) {
return [];
}
$style_attribute = $crawler->filter($filter_selector)->attr('style');
// Explode the style attribute, filtering out the last (empty) element.
$styles = $style_attribute ? explode(';', rtrim($style_attribute, ';')) : [];
// Convert the string into an array of key/value pairs.
$parsed_styles = [];
foreach ($styles as $style) {
if (strlen(trim($style)) > 0) {
[$property, $value] = explode(':', trim($style));
$parsed_styles[$property] = $value;
}
}
return $parsed_styles;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Drupal\Tests\charts\Kernel;
/**
* Test the dimensions element property behavior.
*
* @group charts
*/
class DimensionsTest extends ChartsKernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'charts',
'charts_test',
];
/**
* Test that the set dimensions are correctly added on the element.
*
* @param array $element
* The element to be tested.
* @param string|null $expected_width
* The expected width to be found on the element.
* @param string|null $expected_height
* The expected height to be found on the element.
*
* @dataProvider provideChartElements
*/
public function testDimensions(array $element, string $expected_width = NULL, string $expected_height = NULL) {
$styles = $this->getChartStyle($element);
$width = $styles['width'] ?? NULL;
$height = $styles['height'] ?? NULL;
$this->assertEquals($expected_width, $width);
$this->assertEquals($expected_height, $height);
}
/**
* Provides chart elements for the test dimensions test.
*
* @return array[]
* the chart elements.
*/
public function provideChartElements(): array {
// A simple chart element with no dimensions set.
$element = [
'#type' => 'chart',
'#chart_type' => 'column',
'series' => [
'#type' => 'chart_data',
'#title' => '5.0.x',
'#data' => [257, 235, 325, 340],
'#color' => '#1d84c3',
],
];
// Element with the dimensions set.
$element_filled_dimensions = $element + [
'#width' => 50,
'#width_units' => '%',
'#height' => 225,
'#height_units' => 'px',
];
return [
'empty dimensions' => [$element, NULL, NULL],
'filled dimensions' => [$element_filled_dimensions, '50%', '225px'],
];
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Drupal\Tests\charts\Traits;
/**
* Provides a trait to update the configuration.
*/
trait ConfigUpdateTrait {
/**
* Updates the foo configuration.
*
* @param string $value
* The value to set.
* @param string $library
* Library to be rendered.
*/
protected function updateFooConfiguration(string $value, $library = 'charts_test_library'): void {
$config = $this->config('charts.settings');
$settings = $config->get('charts_default_settings');
$settings['library'] = $library;
$settings['library_config'] = ['foo' => $value];
$config->set('charts_default_settings', $settings);
$config->save();
}
}