<?php
/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/


require_once dirname(__FILE__).'/../common/testWidgets.php';

/**
 * @backup dashboard, globalmacro
 *
 * @dataSource AllItemValueTypes
 *
 * @onBefore prepareHoneycombWidgetData
 */
class testDashboardHoneycombWidget extends testWidgets {

	/**
	 * Attach MessageBehavior, TagBehavior and TableBehavior to the test.
	 *
	 * @return array
	 */
	public function getBehaviors() {
		return [
			CMessageBehavior::class,
			CTagBehavior::class,
			CTableBehavior::class
		];
	}

	/**
	 * Ids of created Dashboards for Honeycomb widget check.
	 */
	protected static $dashboardid;

	/**
	 * Hash before TEST_BAD scenario.
	 */
	protected static $old_hash;

	/**
	 * Widget amount before create/update.
	 */
	protected static $old_widget_count;

	/**
	 * Id of dashboard for update scenarios.
	 */
	protected static $disposable_dashboard_id;

	const DASHBOARD_FOR_MACRO_FUNCTIONS = 'Dashboard for testing macro functions';
	const WIDGET_FOR_MACRO_FUNCTIONS = 'Widget for testing macro functions';

	public static function prepareHoneycombWidgetData() {
		CDataHelper::call('hostgroup.create', [
			[
				'name' => 'Maintenance group'
			],
			[
				'name' => 'Host with tags'
			],
			[
				'name' => 'Items with tags'
			]
		]);
		$groupids = CDataHelper::getIds('name');

		$response = CDataHelper::createHosts([
			[
				'host' => 'Host for honeycomb 1',
				'groups' => [['groupid' => 4]], // Zabbix servers.
				'items' => [
					[
						'name' => 'Numeric for honeycomb 1',
						'key_' => 'num_honey_1',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					]
				]
			],
			[
				'host' => 'Display',
				'groups' => [['groupid' => 4]], // Zabbix servers.
				'items' => [
					[
						'name' => 'Display item 1',
						'key_' => 'honey_display_1',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					],
					[
						'name' => 'Display item 2',
						'key_' => 'honey_display_2',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					],
					[
						'name' => 'Display item 3',
						'key_' => 'honey_display_3',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					],
					[
						'name' => 'Display item 4',
						'key_' => 'honey_display_4',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					],
					[
						'name' => 'Display item 5',
						'key_' => 'honey_display_5',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					]
				]
			],
			[
				'host' => 'Host for maintenance filter',
				'groups' => [['groupid' => $groupids['Maintenance group']]],
				'items' => [
					[
						'name' => 'Maintenance item',
						'key_' => 'maintenance_1',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					]
				]
			],
			[
				'host' => 'Host with tags',
				'groups' => [['groupid' => $groupids['Host with tags']]],
				'tags' => [
					[
						'tag' => 'host_tag_1',
						'value' => 'host_val_1'
					]
				],
				'items' => [
					[
						'name' => 'Host tag item',
						'key_' => 'host_tag_1',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64
					]
				]
			],
			[
				'host' => 'Host with items with tags',
				'groups' => [['groupid' => $groupids['Items with tags']]],
				'items' => [
					[
						'name' => 'Item tag 1',
						'key_' => 'item_tag_1',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64,
						'tags' => [
							[
								'tag' => 'item_tag_1',
								'value' => 'item_val_1'
							]
						]
					],
					[
						'name' => 'Item tag 2',
						'key_' => 'item_tag_2',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64,
						'tags' => [
							[
								'tag' => 'item_tag_2',
								'value' => 'item_val_2'
							]
						]
					],
					[
						'name' => 'Item tag 3',
						'key_' => 'item_tag_3',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64,
						'tags' => [
							[
								'tag' => 'item_tag_3',
								'value' => 'item_val_3'
							]
						]
					],
					[
						'name' => 'Item tag 4',
						'key_' => 'item_tag_4',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64,
						'tags' => [
							[
								'tag' => 'item_tag_1',
								'value' => 'item_val_1'
							]
						]
					],
					[
						'name' => 'Item tag 5',
						'key_' => 'item_tag_5',
						'type' => ITEM_TYPE_TRAPPER,
						'value_type' => ITEM_VALUE_TYPE_UINT64,
						'tags' => [
							[
								'tag' => 'item_tag_1',
								'value' => 'item_val_5'
							]
						]
					]
				]
			]
		]);
		$itemids = $response['itemids'];
		$display_hostid = $response['hostids']['Display'];
		$maintenance_hostid = $response['hostids']['Host for maintenance filter'];

		foreach ([100, 200, 300, 400, 500] as $i => $value) {
			CDataHelper::addItemData($itemids['Display:honey_display_'.($i + 1)], $value);
		}

		// Items ids that used in filtering scenario.
		$filtered_items = [
			$itemids['Host for maintenance filter:maintenance_1'],
			$itemids['Host with tags:host_tag_1'],
			$itemids['Host with items with tags:item_tag_1'],
			$itemids['Host with items with tags:item_tag_2'],
			$itemids['Host with items with tags:item_tag_3'],
			$itemids['Host with items with tags:item_tag_4'],
			$itemids['Host with items with tags:item_tag_5']
		];

		foreach ($filtered_items as $itemid) {
			CDataHelper::addItemData($itemid, 100);
		}

		// Create Maintenance and host in maintenance.
		$maintenances = CDataHelper::call('maintenance.create', [
			[
				'name' => 'Honeycomb host maintenance',
				'active_since' => time() - 1000,
				'active_till' => time() + 31536000,
				'groups' => [['groupid' => $groupids['Maintenance group']]],
				'timeperiods' => [[]]
			]
		]);
		$maintenanceid = $maintenances['maintenanceids'][0];

		DBexecute('UPDATE hosts SET maintenanceid='.zbx_dbstr($maintenanceid).
				', maintenance_status=1, maintenance_type='.MAINTENANCE_TYPE_NORMAL.', maintenance_from='.zbx_dbstr(time()-1000).
				' WHERE hostid='.zbx_dbstr($maintenance_hostid)
		);

		CDataHelper::call('dashboard.create', [
			[
				'name' => 'Dashboard for creating honeycomb widgets',
				'auto_start' => 0,
				'pages' => [[]]
			],
			[
				'name' => 'Dashboard for simple updating honeycomb widget',
				'auto_start' => 0,
				'pages' => [
					[
						'widgets' => [
							[
								'type' => 'honeycomb',
								'name' => 'UpdateHoneycomb',
								'x' => 0,
								'y' => 0,
								'width' => 12,
								'height' => 5,
								'fields' => [
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Numeric for honeycomb 1'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'GZGZG'
									]
								]
							]
						]
					]
				]
			],
			[
				'name' => 'Dashboard for canceling honeycomb widget',
				'auto_start' => 0,
				'pages' => [
					[
						'widgets' => [
							[
								'type' => 'honeycomb',
								'name' => 'CancelHoneycomb',
								'x' => 0,
								'y' => 0,
								'width' => 12,
								'height' => 5,
								'fields' => [
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Numeric for honeycomb 1'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'BUBUA'
									]
								]
							]
						]
					]
				]
			],
			[
				'name' => 'Dashboard for deleting honeycomb widget',
				'auto_start' => 0,
				'pages' => [
					[
						'widgets' => [
							[
								'type' => 'honeycomb',
								'name' => 'DeleteHoneycomb',
								'x' => 0,
								'y' => 0,
								'width' => 12,
								'height' => 5,
								'fields' => [
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Numeric for honeycomb 1'
									]
								]
							]
						]
					]
				]
			],
			[
				'name' => 'Dashboard for Honeycomb screenshot',
				'auto_start' => 0,
				'pages' => [
					[
						'name' => '3 dots',
						'widgets' => [
							[
								'type' => 'honeycomb',
								'x' => 0,
								'y' => 0,
								'width' => 6,
								'height' => 2,
								'view_mode' => 0,
								'fields' => [
									[
										'type' => '3',
										'name' => 'hostids.0',
										'value' => $display_hostid
									],
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Display item 1'
									],
									[
										'type' => 1,
										'name' => 'items.1',
										'value' => 'Display item 2'
									],
									[
										'type' => 1,
										'name' => 'items.2',
										'value' => 'Display item 3'
									],
									[
										'type' => 1,
										'name' => 'items.3',
										'value' => 'Display item 4'
									],
									[
										'type' => 1,
										'name' => 'items.4',
										'value' => 'Display item 5'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'LEMLX'
									]
								]
							]
						]
					],
					[
						'name' => 'items and 3 dots',
						'widgets' => [
							[
								'type' => 'honeycomb',
								'x' => 0,
								'y' => 0,
								'width' => 9,
								'height' => 2,
								'view_mode' => 0,
								'fields' => [
									[
										'type' => '3',
										'name' => 'hostids.0',
										'value' => $display_hostid
									],
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Display item 1'
									],
									[
										'type' => 1,
										'name' => 'items.1',
										'value' => 'Display item 2'
									],
									[
										'type' => 1,
										'name' => 'items.2',
										'value' => 'Display item 3'
									],
									[
										'type' => 1,
										'name' => 'items.3',
										'value' => 'Display item 4'
									],
									[
										'type' => 1,
										'name' => 'items.4',
										'value' => 'Display item 5'
									],
									[
										'type' => 1,
										'name' => 'bg_color',
										'value' => 'FFEBEE'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'BRZEV'
									]
								]
							]
						]
					],
					[
						'name' => '5 items grouped',
						'widgets' => [
							[
								'type' => 'honeycomb',
								'x' => 0,
								'y' => 0,
								'width' => 13,
								'height' => 4,
								'view_mode' => 0,
								'fields' => [
									[
										'type' => '3',
										'name' => 'hostids.0',
										'value' => $display_hostid
									],
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Display item 1'
									],
									[
										'type' => 1,
										'name' => 'items.1',
										'value' => 'Display item 2'
									],
									[
										'type' => 1,
										'name' => 'items.2',
										'value' => 'Display item 3'
									],
									[
										'type' => 1,
										'name' => 'items.3',
										'value' => 'Display item 4'
									],
									[
										'type' => 1,
										'name' => 'items.4',
										'value' => 'Display item 5'
									],
									[
										'type' => 0,
										'name' => 'primary_label_bold',
										'value' => 1
									],
									[
										'type' => 1,
										'name' => 'primary_label_color',
										'value' => '66BB6A'
									],
									[
										'type' => 1,
										'name' => 'secondary_label_color',
										'value' => '0040FF'
									],
									[
										'type' => 1,
										'name' => 'bg_color',
										'value' => 'A1887F'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'WUFXS'
									]
								]
							]
						]
					],
					[
						'name' => 'Long horizontal line with interpolation',
						'widgets' => [
							[
								'type' => 'honeycomb',
								'x' => 0,
								'y' => 0,
								'width' => 29,
								'height' => 3,
								'view_mode' => 0,
								'fields' => [
									[
										'type' => '3',
										'name' => 'hostids.0',
										'value' => $display_hostid
									],
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Display item 1'
									],
									[
										'type' => 1,
										'name' => 'items.1',
										'value' => 'Display item 2'
									],
									[
										'type' => 1,
										'name' => 'items.2',
										'value' => 'Display item 3'
									],
									[
										'type' => 1,
										'name' => 'items.3',
										'value' => 'Display item 4'
									],
									[
										'type' => 1,
										'name' => 'items.4',
										'value' => 'Display item 5'
									],
									[
										'type' => 0,
										'name' => 'interpolation',
										'value' => 1
									],
									[
										'type' => 1,
										'name' => 'thresholds.0.color',
										'value' => 'FF465C'
									],
									[
										'type' => 1,
										'name' => 'thresholds.0.threshold',
										'value' => '100'
									],
									[
										'type' => 1,
										'name' => 'thresholds.1.color',
										'value' => 'FFD54F'
									],
									[
										'type' => 1,
										'name' => 'thresholds.1.threshold',
										'value' => '500'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'NEFEX'
									],
									[
										'type' => 0,
										'name' => 'primary_label_type',
										'value' => 1
									],
									[
										'type' => 0,
										'name' => 'primary_label_decimal_places',
										'value' => 0
									],
									[
										'type' => 0,
										'name' => 'primary_label_size_type',
										'value' => 1
									],
									[
										'type' => 0,
										'name' => 'primary_label_size',
										'value' => 50
									],
									[
										'type' => 0,
										'name' => 'primary_label_bold',
										'value' => 1
									],
									[
										'type' => 0,
										'name' => 'secondary_label_decimal_places',
										'value' => 0
									],
									[
										'type' => 0,
										'name' => 'secondary_label_size_type',
										'value' => 1
									],
									[
										'type' => 0,
										'name' => 'secondary_label_size',
										'value' => 20
									]
								]
							]
						]
					],
					[
						'name' => 'Default vertical line',
						'widgets' => [
							[
								'type' => 'honeycomb',
								'x' => 0,
								'y' => 0,
								'width' => 9,
								'height' => 7,
								'view_mode' => 0,
								'fields' => [
									[
										'type' => '3',
										'name' => 'hostids.0',
										'value' => $display_hostid
									],
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Display item 1'
									],
									[
										'type' => 1,
										'name' => 'items.1',
										'value' => 'Display item 2'
									],
									[
										'type' => 1,
										'name' => 'items.2',
										'value' => 'Display item 3'
									],
									[
										'type' => 1,
										'name' => 'items.3',
										'value' => 'Display item 4'
									],
									[
										'type' => 1,
										'name' => 'items.4',
										'value' => 'Display item 5'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'TOQVG'
									]
								]
							]
						]
					]
				]
			],
			[
				'name' => self::DASHBOARD_FOR_MACRO_FUNCTIONS,
				'auto_start' => 0,
				'pages' => [
					[
						'widgets' => [
							[
								'type' => 'honeycomb',
								'name' => self::WIDGET_FOR_MACRO_FUNCTIONS,
								'x' => 0,
								'y' => 0,
								'width' => 12,
								'height' => 5,
								'fields' => [
									[
										'type' => 1,
										'name' => 'items.4',
										'value' => 'Display item 5'
									]
								]
							]
						]
					]
				]
			]
		]);
		self::$dashboardid = CDataHelper::getIds('name');

		CDataHelper::call('usermacro.createglobal', [
			[
				'macro' => '{$TEXT}',
				'value' => 'text_macro'
			],
			[
				'macro' => '{$SECRET_TEXT}',
				'type' => 1,
				'value' => 'secret_macro'
			],
			[
				'macro' => self::USER_MACRO,
				'value' => self::USER_MACRO_VALUE
			],
			[
				'macro' => self::USER_SECRET_MACRO,
				'type' => 1,
				'value' => self::USER_MACRO_VALUE
			],
			[
				'macro' => self::MACRO_CHAR,
				'value' => self::MACRO_CHAR_VALUE
			],
			[
				'macro' => self::MACRO_HTML_ENCODE,
				'value' => self::MACRO_HTML_ENCODE_VALUE
			],
			[
				'macro' => self::MACRO_HTML_DECODE,
				'value' => self::MACRO_HTML_DECODE_VALUE
			],
			[
				'macro' => self::MACRO_URL_ENCODE,
				'value' => self::MACRO_URL_ENCODE_VALUE
			],
			[
				'macro' => self::MACRO_URL_DECODE,
				'value' => self::MACRO_URL_DECODE_VALUE
			]
		]);
	}

	public function testDashboardHoneycombWidget_Layout() {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid['Dashboard for creating honeycomb widgets'])->waitUntilReady();

		$dashboard = CDashboardElement::find()->waitUntilReady()->one();
		$form = $dashboard->edit()->addWidget()->asForm();
		$form->fill(['Type' => CFormElement::RELOADABLE_FILL('Honeycomb')]);

		// Check fields maxlengths.
		$maxlengths = [
			'Name' => 255,
			'id:host_tags_0_tag' => 255,
			'id:host_tags_0_value' => 255,
			'id:item_tags_0_tag' => 255,
			'id:item_tags_0_value' => 255,
			'id:primary_label' => 2048,
			'id:primary_label_decimal_places' => 1,
			'id:primary_label_units' => 2048,
			'id:primary_label_size' => 3,
			'id:secondary_label' => 2048,
			'id:secondary_label_decimal_places' => 1,
			'id:secondary_label_units' => 2048,
			'id:secondary_label_size' => 3
		];

		foreach ($maxlengths as $field => $maxlength) {
			$this->assertEquals($maxlength, $form->getField($field)->getAttribute('maxlength'));
		}

		// Check default values.
		$default_values = [
			'Name' => '',
			'Refresh interval' => 'Default (1 minute)',
			'Host groups' => '',
			'Hosts' => '',
			'Host tags' => 'And/Or',
			'Item patterns' => '',
			'Item tags' => 'And/Or',
			'Show hosts in maintenance' => false,
			'Advanced configuration' => false,
			'id:host_tags_0_tag' => '',
			'id:host_tags_0_value' => '',
			'id:item_tags_0_tag' => '',
			'id:item_tags_0_value' => '',
			'Show' => ['Primary label', 'Secondary label'],
			'id:primary_label_type_0' => 'Text',
			'Text' => '{HOST.NAME}',
			'id:primary_label_size_type_0' => 'Auto',
			'id:primary_label_bold' => false,
			'id:secondary_label_type_1' => 'Value',
			'id:secondary_label_decimal_places' => 2,
			'id:secondary_label_size_type_0' => 'Auto',
			'id:secondary_label_bold' => true,
			'id:secondary_label_units' => '',
			'id:secondary_label_units_pos' => 'After value',
			'id:interpolation' => false
		];

		$form->checkValue($default_values);

		// Check color picker default values.
		$color_pickers = [
			'id:lbl_primary_label_color',   // Primary label color.
			'id:lbl_secondary_label_color', // Secondary label color.
			'id:lbl_bg_color'				// Background color.
		];
		foreach ($color_pickers as $id) {
			$color_picker = $form->query($id)->one();
			$this->assertEquals('', $color_picker->getValue());
			$this->assertEquals('Use default', $color_picker->query('xpath:./../button')->one()->getAttribute('title'));
		}

		// Check Select popup dropdowns for Host groups and Hosts.
		$popup_menu_selector = 'xpath:.//button[contains(@class, "zi-chevron-down")]';
		$host_groups = ['Host groups', 'Widget'];
		$hosts = ['Hosts', 'Widget', 'Dashboard'];

		foreach (['Host groups', 'Hosts'] as $label) {
			$field = $form->getField($label);

			// Check Select dropdown menu button.
			$menu_button = $field->query($popup_menu_selector)->asPopupButton()->one();
			$this->assertEquals(($label === 'Host groups') ? $host_groups : $hosts,
					$menu_button->getMenu()->getItems()->asText()
			);

			// After selecting Dashboard from dropdown menu, check hint and field value.
			if ($label === 'Hosts') {
				$menu_button->select('Dashboard');
				$form->checkValue(['Hosts' => 'Dashboard']);
				$this->assertTrue($field->query('xpath:.//span[@data-hintbox-contents="Dashboard is used as data source."]')
						->one()->isVisible()
				);
			}

			// After selecting Widget from dropdown menu, check overlay dialog appearance and title.
			$menu_button->select('Widget');
			$dialogs = COverlayDialogElement::find()->all();
			$this->assertEquals('Widget', $dialogs->last()->waitUntilReady()->getTitle());
			$dialogs->last()->close(true);
		}

		// After clicking on Select button, check overlay dialog appearance and title.
		foreach (['Host groups', 'Hosts', 'Item patterns'] as $label) {
			$field = $form->getField($label);
			$field->query('button:Select')->waitUntilCLickable()->one()->click();
			$dialogs = COverlayDialogElement::find()->all();
			$label = ($label === 'Item patterns') ? 'Items' : $label;
			$this->assertEquals($label, $dialogs->last()->waitUntilReady()->getTitle());
			$dialogs->last()->close(true);
		}

		// Check Show checkboxes and their values.
		$show = $form->getField('Show');
		$this->assertEquals(['Primary label', 'Secondary label'], $show->getLabels()->asText());
		$this->assertTrue($show->isEnabled());

		// Check Add/Remove buttons for Hosts and Items tags tables.
		foreach (['tags_table_host_tags', 'tags_table_item_tags'] as $id) {
			$this->assertEquals(2, $form->query('id', $id)->one()->query('button', ['Add', 'Remove'])
					->all()->filter(CElementFilter::CLICKABLE)->count()
			);
		}

		// Fields layout after Advanced configuration expand.
		$form->fill(['Advanced configuration' => true]);
		$this->query('id:lbl_bg_color')->one()->waitUntilVisible();

		// Check Primary and Secondary label fields that disappear after checking them.
		$advanced_configuration = [
			'Primary label' => '{HOST.NAME}',
			'Secondary label' => '{{ITEM.LASTVALUE}.fmtnum(2)}'
		];

		foreach ($advanced_configuration as $label => $text) {
			$container = $form->getFieldContainer($label);
			$this->assertTrue($container->isVisible());

			// Type radio button. After changing them, some fields appears and other disappear.
			$label_id = ($label === 'Primary label') ? 'primary_label_' : 'secondary_label_';
			$type_label = $container->query('id', $label_id.'type')->asSegmentedRadio()->one();
			$this->assertEquals(['Text', 'Value'], $type_label->getLabels()->asText());

			foreach (['Text', 'Value'] as $type_values) {
				$type_label->select($type_values);

				if ($type_values === 'Text') {
					$this->assertEquals($text, $container->query('xpath:.//textarea')->one()->getText());

					// Check hintboxes.
					$hint_text = "Supported macros:".
						"\n{HOST.*}".
						"\n{ITEM.*}".
						"\n{INVENTORY.*}".
						"\nUser macros";

					$form->getLabel('Text')->query('xpath:./button[@data-hintbox]')->one()->click();
					$hint = $this->query('xpath://div[@data-hintboxid]')->waitUntilVisible();
					$this->assertEquals($hint_text, $hint->one()->getText());
					$hint->one()->query('xpath:.//button[@class="btn-overlay-close"]')->one()->click();
					$hint->waitUntilNotPresent();
				}
				else {
					// New fields and check box appears after selecting Type - Value.
					$units_checkbox = $container->query('id', $label_id.'units_show')->asCheckbox()->one();
					$units_input = $container->query('xpath:.//div[contains(@class, "form-field")]//input[contains(@id,'.
							' "_label_units")]')->one();
					$position_dropdown = $container->query('id', $label_id.'units_pos')->asDropdown()->one();
					$this->assertEquals(['Before value', 'After value'], $position_dropdown->getOptions()->asText());

					// Checking out Units checkbox - disable Units fields.
					foreach ([false, true] as $status) {
						$units_checkbox->set($status);
						$this->assertTrue($units_checkbox->isChecked($status));
						$this->assertTrue($units_input->isEnabled($status));
						$this->assertTrue($position_dropdown->isEnabled($status));
					}
				}
			}

			// Check size radio button and appearance of new field after clicking on Custom.
			$size = $container->query('xpath:.//ul[contains(@id, "label_size_type")]')->asSegmentedRadio()->one();
			$this->assertEquals(['Auto', 'Custom'], $size->getLabels()->asText());

			// Primary and Secondary label has different values in size custom field.
			$size_input_value = ($label === 'Primary label') ? '20' : '30';

			// After clicking on Custom button, new input field appears.
			$size_input_selector = $container->query('id', $label_id.'size')->one();
			$this->assertFalse($size_input_selector->isVisible());
			$size->select('Custom');
			$this->assertTrue($size_input_selector->isVisible());
			$this->assertEquals($size_input_value, $size_input_selector->getAttribute('value'));

			// After clicking on Auto, input field disappear.
			$size->select('Auto');
			$this->assertFalse($size_input_selector->isVisible());

			// Check Bold checkbox. Primary label bold - unchecked. Secondary label bold - checked-in.
			$bold = $container->query('xpath:.//input[contains(@id, "_label_bold")]')->asCheckbox()->one();
			$this->assertTrue($bold->isChecked($label === 'Secondary label'));

			// Uncheck Primary/Secondary label.
			$show->set($label, false);
			$this->assertFalse($container->isVisible());
		}

		// Thresholds warning message.
		$form->getLabel('Thresholds')->query('xpath:./button[@data-hintbox-contents]')->one()->click();
		$warning = $this->query('xpath://div[@data-hintboxid]')->waitUntilVisible();
		$this->assertEquals('This setting applies only to numeric data.', $warning->one()->getText());
		$warning->one()->query('xpath:.//button[@class="btn-overlay-close"]')->one()->click();
		$warning->waitUntilNotPresent();

		// Color interpolation checkbox should be disabled.
		$this->assertFalse($form->query('id:interpolation')->asCheckbox()->one()->isEnabled());

		// Threshold table with adding and removing lines.
		$table = $form->query('id:thresholds-table')->asTable()->one();
		$this->assertEquals(['', 'Threshold', 'Action'], $table->getHeadersText());

		// Check added threshold colors.
		$table->query('button:Add')->one()->click();
		$colorpicker = $table->query('xpath:.//input[@id="thresholds_0_color"]/..')->asColorPicker()->one();
		$this->assertEquals('FF465C', $colorpicker->getValue());
		$this->assertTrue($colorpicker->isClickable());

		// Check added threshold input field.
		$threshold_input = $table->query('xpath:.//input[@type="text"]')->one();
		$this->assertEquals(255, $threshold_input->getAttribute('maxlength'));

		// Click on Remove button in Threshold, and check that it disappeared with input and colorpicker.
		$remove = $table->query('button:Remove')->one();
		$remove->click();
		$this->assertFalse($colorpicker->isVisible());
		$this->assertFalse($threshold_input->isVisible());
		$this->assertFalse($remove->isVisible());

		COverlayDialogElement::find()->one()->close();
		$dashboard->cancelEditing();
	}

	public static function getCreateData() {
		return [
			// #0.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Name' => 'No item',
						'Item patterns' => ''
					],
					'error_message' => [
						'Invalid parameter "Item patterns": cannot be empty.'
					]
				]
			],
			// #1.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test',
						'id:show_1' => false, // Show - Primary label.
						'id:show_2' => false // Show - Secondary label.
					],
					'error_message' => [
						'Invalid parameter "Show": at least one option must be selected.'
					]
				]
			],
			// #2.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test',
						'Background colour' => 'tests1'
					],
					'error_message' => [
						'Invalid parameter "Background colour": a hexadecimal colour code (6 symbols) is expected.'
					]
				]
			],
			// #3.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test'
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'TESTS1'
						]
					],
					'error_message' => [
						'Invalid parameter "Thresholds/1/color": a hexadecimal colour code (6 symbols) is expected.'
					]
				]
			],
			// #4.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test'
					],
					'thresholds' => [
						[
							'threshold' => 'test',
							'color' => 'FF465C'
						]
					],
					'error_message' => [
						'Invalid parameter "Thresholds/1/threshold": a number is expected.'
					]
				]
			],
			// #5.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test'
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'FF465C'
						],
						[
							'threshold' => '1',
							'color' => 'TESTS1'
						]
					],
					'error_message' => [
						'Invalid parameter "Thresholds/2/color": a hexadecimal colour code (6 symbols) is expected.'
					]
				]
			],
			// #6.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test'
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'FF465C'
						],
						[
							'threshold' => 'test',
							'color' => 'FF465C'
						]
					],
					'error_message' => [
						'Invalid parameter "Thresholds/2/threshold": a number is expected.'
					]
				]
			],
			// #7.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test'
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'FF465C'
						],
						[
							'threshold' => '1',
							'color' => 'FF465C'
						]
					],
					'error_message' => [
						'Invalid parameter "Thresholds/2": value (threshold)=(1) already exists.'
					]
				]
			],
			// #8.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test',
						'id:primary_label' => '' // Primary label text field.
					],
					'error_message' => [
						'Invalid parameter "Primary label: Text": cannot be empty.'
					]
				]
			],
			// #9.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test',
						'id:primary_label_size_type' => 'Custom', // Primary label Size - custom.
						'id:primary_label_size' => '' // Primary label Custom size input field.
					],
					'error_message' => [
						'Invalid parameter "Primary label: Size": value must be one of 1-100.'
					]
				]
			],
			// #10.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test',
						'id:primary_label_type' => 'Value',
						'id:primary_label_decimal_places' => 9,
						'id:secondary_label_type' => 'Value',
						'id:secondary_label_decimal_places' => 9
					],
					'error_message' => [
						'Invalid parameter "Primary label: Decimal places": value must be one of 0-6.',
						'Invalid parameter "Secondary label: Decimal places": value must be one of 0-6.'
					]
				]
			],
			// #11.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => 'test',
						'id:secondary_label_type' => 'Text',
						'id:primary_label_size_type' => 'Custom', // Primary label Size - custom.
						'id:secondary_label_size_type' => 'Custom', // Secondary label Size - custom.
						'id:primary_label_size' => 'text', // Primary label Custom size input field.
						'id:secondary_label_size' => 'text',  // Secondary label Custom size input field.
						'id:primary_label' => '', // Primary label text field.
						'id:secondary_label' => '' // Secondary label text field.
					],
					'error_message' => [
						'Invalid parameter "Primary label: Text": cannot be empty.',
						'Invalid parameter "Primary label: Size": value must be one of 1-100.',
						'Invalid parameter "Secondary label: Text": cannot be empty.',
						'Invalid parameter "Secondary label: Size": value must be one of 1-100.'
					]
				]
			],
			// #12.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Item patterns' => '',
						'id:secondary_label_type' => 'Text',
						'id:primary_label_size_type' => 'Custom', // Primary label Size - custom.
						'id:secondary_label_size_type' => 'Custom', // Secondary label Size - custom.
						'id:primary_label_size' => '🙂🙃', // Primary label Custom size input field.
						'id:secondary_label_size' => '🙂🙃',  // Secondary label Custom size input field.
						'id:primary_label' => '', // Primary label text field.
						'id:secondary_label' => '', // Secondary label text field.
						'xpath:.//input[@id="primary_label_color"]/..' => 'TESTS1', // Primary label Color.
						'xpath:.//input[@id="secondary_label_color"]/..' => 'TESTS2' // Secondary label Color.
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'TESTS1'
						]
					],
					'error_message' => [
						'Invalid parameter "Item patterns": cannot be empty.',
						'Invalid parameter "Primary label: Text": cannot be empty.',
						'Invalid parameter "Primary label: Size": value must be one of 1-100.',
						'Invalid parameter "Primary label: Colour": a hexadecimal colour code (6 symbols) is expected.',
						'Invalid parameter "Secondary label: Text": cannot be empty.',
						'Invalid parameter "Secondary label: Size": value must be one of 1-100.',
						'Invalid parameter "Secondary label: Colour": a hexadecimal colour code (6 symbols) is expected.',
						'Invalid parameter "Thresholds/1/color": a hexadecimal colour code (6 symbols) is expected.',
						'Invalid parameter "Thresholds/1/color": a hexadecimal colour code (6 symbols) is expected.'
					]
				]
			],
			// #13.
			[
				[
					'fields' => [
						'Name' => 'With existing item, hosts and hostgroup',
						'Item patterns' => 'Numeric for honeycomb 1',
						'Host groups' => 'Zabbix servers',
						'Hosts' => 'Host for honeycomb 1'
					]
				]
			],
			// #14.
			[
				[
					'fields' => [
						'Name' => '',
						'Item patterns' => 'Numeric for honeycomb 1',
						'id:show_2' => false, // Show - Primary label.
						'id:primary_label' => '{$RANDOM}, some text, {TIME}, 12345, !@#$%^&*, {#WHY}',
						'id:primary_label_size_type' => 'Custom', // Primary label Size - custom.
						'id:primary_label_size' => 99,
						'xpath:.//input[@id="primary_label_color"]/..' => '81C784' // Primary label Color.
					]
				]
			],
			// #15.
			[
				[
					'fields' => [
						'Name' => 'Secondary label only with Text, color, custom.',
						'Item patterns' => 'Numeric for honeycomb 1',
						'id:show_1' => false, // Show - Primary label.
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{$RANDOM}, some text, {TIME}, 12345, !@#$%^&*, {#WHY}',
						'id:secondary_label_size_type' => 'Custom', // Primary label Size - custom.
						'id:secondary_label_size' => 99,
						'xpath:.//input[@id="secondary_label_color"]/..' => '81C784' // Secondary label Color.
					]
				]
			],
			// #16.
			[
				[
					'fields' => [
						'Name' => 'Secondary and primary labels only with values.',
						'Item patterns' => 'Numeric for honeycomb 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => 2,
						'id:secondary_label_decimal_places' => 2,
						'id:primary_label_bold' => true,
						'id:secondary_label_bold' => true,
						'xpath:.//input[@id="primary_label_color"]/..' => '81C784', // Primary label Color.
						'xpath:.//input[@id="secondary_label_color"]/..' => '81C784', // Primary label Color.
						'id:primary_label_units_pos' => 'Before value',
						'id:secondary_label_units_pos' => 'Before value',
						'id:primary_label_units' => 'primary',
						'id:secondary_label_units' => 'secondary'
					]
				]
			],
			// #17.
			[
				[
					'fields' => [
						'Name' => 'Dashboard in Hosts and enabled show maintenance',
						'Hosts' => 'Dashboard',
						'Item patterns' => 'Numeric for honeycomb 1',
						'Show hosts in maintenance' => true
					]
				]
			],
			// #18.
			[
				[
					'fields' => [
						'Name' => 'Different items pattern',
						'Item patterns' => [
							'Numeric for honeycomb 1',
							'random_value',
							'*',
							'<$%^&*#@^',
							'<script>alert("hi!");</script>',
							'test тест 测试 テスト ทดสอบ'
						]
					]
				]
			],
			// #19.
			[
				[
					'fields' => [
						'Name' => 'Enabled color interpolation',
						'Item patterns' => 'Numeric for honeycomb 1'
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'FF465C'
						],
						[
							'threshold' => '2',
							'color' => 'FFFF00'
						]
					]
				]
			],
			// #20.
			[
				[
					'fields' => [
						'Name' => 'Disabled color interpolation',
						'Item patterns' => 'Numeric for honeycomb 1',
						'id:interpolation' => false
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'FF465C'
						],
						[
							'threshold' => '2',
							'color' => 'FFFF00'
						]
					]
				]
			],
			// #21.
			[
				[
					'fields' => [
						'Name' => 'Correct background color',
						'Item patterns' => 'Numeric for honeycomb 1',
						'Background colour' => 'B2DFDB'
					]
				]
			],
			// #22.
			[
				[
					'fields' => [
						'Name' => 'Host and items tags 🙂🙃',
						'Item patterns' => 'Numeric for honeycomb 1'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'value', 'value' => '12345', 'operator' => 'Contains'],
							['name' => '@#$%@', 'value' => 'a1b2c3d4', 'operator' => 'Equals'],
							['name' => 'AvF%21', 'operator' => 'Exists'],
							['name' => '_', 'operator' => 'Does not exist'],
							['name' => 'кириллица', 'value' => 'BcDa', 'operator' => 'Does not equal'],
							['name' => 'aaa6 😅', 'value' => 'bbb6 😅', 'operator' => 'Does not contain']
						],
						'host_tags' => [
							['name' => 'value', 'value' => '12345', 'operator' => 'Contains'],
							['name' => '@#$%@', 'value' => 'a1b2c3d4', 'operator' => 'Equals'],
							['name' => 'AvF%21', 'operator' => 'Exists'],
							['name' => '_', 'operator' => 'Does not exist'],
							['name' => 'кириллица', 'value' => 'BcDa', 'operator' => 'Does not equal'],
							['name' => 'aaa6 😅', 'value' => 'bbb6 😅', 'operator' => 'Does not contain']
						]
					]
				]
			],
			// #23.
			[
				[
					'fields' => [
						'Name' => 'All available fields filled',
						'Item patterns' => 'Numeric for honeycomb 1',
						'Refresh interval' => 'No refresh',
						'Host groups' => 'Zabbix servers',
						'Hosts' => 'Host for honeycomb 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Text',
						'id:primary_label_decimal_places' => 6,
						'id:secondary_label' => 'some text',
						'id:primary_label_bold' => true,
						'id:secondary_label_bold' => false,
						'id:primary_label_size' => 99,
						'id:secondary_label_size' => 99,
						'xpath:.//input[@id="primary_label_color"]/..' => '81C784', // Primary label Color.
						'xpath:.//input[@id="secondary_label_color"]/..' => '81C784', // Primary label Color.
						'id:primary_label_units_pos' => 'Before value',
						'id:primary_label_units' => 'primary',
						'Background colour' => 'B2DFDB'
					],
					'thresholds' => [
						[
							'threshold' => '1',
							'color' => 'FF465C'
						],
						[
							'threshold' => '2',
							'color' => 'FFFF00'
						]
					]
				]
			]
		];
	}

	/**
	 * Create Honeycomb widget.
	 *
	 * @dataProvider getCreateData
	 */
	public function testDashboardHoneycombWidget_Create($data) {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid['Dashboard for creating honeycomb widgets'])->waitUntilReady();

		// Get hash if expected is TEST_BAD.
		if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
			// Hash before update.
			self::$old_hash = CDBHelper::getHash(self::SQL);
		}
		else {
			self::$old_widget_count = CDashboardElement::find()->waitUntilReady()->one()->getWidgets()->count();
		}

		$dashboard = CDashboardElement::find()->waitUntilReady()->one();
		$this->fillWidgetForm($data, 'create', $dashboard);
		$this->checkWidgetForm($data, 'create', $dashboard);
	}

	/**
	 * Honeycomb widget simple update without any field change.
	 */
	public function testDashboardHoneycombWidget_SimpleUpdate() {
		// Hash before simple update.
		self::$old_hash = CDBHelper::getHash(self::SQL);

		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid['Dashboard for simple updating honeycomb widget'])->waitUntilReady();
		$dashboard = CDashboardElement::find()->one();
		$dashboard->edit()->getWidget('UpdateHoneycomb')->edit()->submit();
		$dashboard->getWidget('UpdateHoneycomb');
		$dashboard->save();
		$this->page->waitUntilReady();
		$this->assertMessage(TEST_GOOD, 'Dashboard updated');

		// Compare old hash and new one.
		$this->assertEquals(self::$old_hash, CDBHelper::getHash(self::SQL));
	}

	/**
	 * Creates the base widget used for the update scenario.
	 */
	public function prepareUpdateHoneycomb() {
		$providedData = $this->getProvidedData();
		$data = reset($providedData);

		// Create a dashboard with the widget for updating.
		$response = CDataHelper::call('dashboard.create', [
			[
				'name' => 'Dashboard for honeycomb update '.md5(serialize($data)),
				'pages' => [
					[
						'widgets' => [
							[
								'type' => 'honeycomb',
								'name' => 'UpdateHoneycomb',
								'x' => 0,
								'y' => 0,
								'width' => 12,
								'height' => 5,
								'fields' => [
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'Numeric for honeycomb 1'
									]
								]
							]
						]
					]
				]
			]
		]);
		self::$disposable_dashboard_id = $response['dashboardids'][0];
	}

	/**
	 * Update Honeycomb widget.
	 *
	 * @onBefore prepareUpdateHoneycomb
	 *
	 * @dataProvider getCreateData
	 */
	public function testDashboardHoneycombWidget_Update($data) {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$disposable_dashboard_id)->waitUntilReady();

		// Get hash if expected is TEST_BAD.
		if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
			// Hash before update.
			self::$old_hash = CDBHelper::getHash(self::SQL);
		}
		else {
			self::$old_widget_count = CDashboardElement::find()->waitUntilReady()->one()->getWidgets()->count();
		}

		$dashboard = CDashboardElement::find()->waitUntilReady()->one();
		$this->fillWidgetForm($data, 'update', $dashboard);
		$this->checkWidgetForm($data, 'update', $dashboard);
	}

	/**
	 * Delete Honeycomb widget.
	 */
	public function testDashboardHoneycombWidget_Delete() {
		$widget_name = 'DeleteHoneycomb';
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid['Dashboard for deleting honeycomb widget'])->waitUntilReady();
		$dashboard = CDashboardElement::find()->one()->waitUntilReady()->edit();
		$widget = $dashboard->getWidget($widget_name);
		$this->assertTrue($widget->isEditable());
		$dashboard->deleteWidget($widget_name);
		$widget->waitUntilNotPresent();
		$dashboard->save();
		$this->page->waitUntilReady();
		$this->assertMessage(TEST_GOOD, 'Dashboard updated');

		// Check that widget is not present on dashboard and in DB.
		$this->assertFalse($dashboard->getWidget($widget_name, false)->isValid());
		$this->assertEquals(0, CDBHelper::getCount('SELECT * FROM widget_field wf'.
				' LEFT JOIN widget w'.
				' ON w.widgetid=wf.widgetid'.
				' WHERE w.name='.zbx_dbstr($widget_name)
		));
	}

	public static function getDisplayData() {
		return [
			// #0.
			[
				[
					'fields' => [
						'Name' => 'Resolved macros',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => '{HOST.NAME} {ITEM.KEY} {ITEM.LASTVALUE}',
						'id:secondary_label' => '{HOST.NAME} {ITEM.KEY} {ITEM.LASTVALUE}'
					],
					'result' => 'Display honey_display_1 100'
				]
			],
			// #1.
			[
				[
					'fields' => [
						'Name' => 'Emoji and special symbols displayed',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => '🙂🙃 āēīõšŗ$%^&*()',
						'id:secondary_label' => '🙂🙃 āēīõšŗ$%^&*()'
					],
					'result' => '🙂🙃 āēīõšŗ$%^&*()'
				]
			],
			// #2.
			[
				[
					'fields' => [
						'Name' => 'Simple text displayed',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => 'Text for testing',
						'id:secondary_label' => 'Text for testing'
					],
					'result' => 'Text for testing'
				]
			],
			// #3.
			[
				[
					'fields' => [
						'Name' => 'User macros displayed {$TEXT}',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => '{$TEXT}',
						'id:secondary_label' => '{$TEXT}'
					],
					'result' => 'text_macro'
				]
			],
			// #4.
			[
				[
					'fields' => [
						'Name' => 'Secret macros displayed {$SECRET_TEXT}',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => '{$SECRET_TEXT}',
						'id:secondary_label' => '{$SECRET_TEXT}'
					],
					'result' => '******'
				]
			],
			// #5.
			[
				[
					'fields' => [
						'Name' => 'LLD macros displayed {#LLD}',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => '{#LLD}',
						'id:secondary_label' => '{#LLD}'
					],
					'result' => '{#LLD}'
				]
			],
			// #6.
			[
				[
					'fields' => [
						'Name' => 'Non existing global macros displayed {HELLO.WORLD}',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => '{HELLO.WORLD}',
						'id:secondary_label' => '{HELLO.WORLD}'
					],
					'result' => '{HELLO.WORLD}'
				]
			],
			// #7.
			[
				[
					'fields' => [
						'Name' => '123',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => '6',
						'id:secondary_label_decimal_places' => '6'
					],
					'result' => '100.000000'
				]
			],
			// #8.
			[
				[
					'fields' => [
						'Name' => 'Value decimal 0',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => '0',
						'id:secondary_label_decimal_places' => '0'
					],
					'result' => '100'
				]
			],
			// #9.
			[
				[
					'fields' => [
						'Name' => 'Before displayed units',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => '0',
						'id:secondary_label_decimal_places' => '0',
						'id:primary_label_units_pos' => 'Before value',
						'id:secondary_label_units_pos' => 'Before value',
						'id:primary_label_units' => 'before',
						'id:secondary_label_units' => 'before'
					],
					'result' => 'before 100'
				]
			],
			// #10.
			[
				[
					'fields' => [
						'Name' => 'After displayed units',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => '0',
						'id:secondary_label_decimal_places' => '0',
						'id:primary_label_units_pos' => 'After value',
						'id:secondary_label_units_pos' => 'After value',
						'id:primary_label_units' => 'after',
						'id:secondary_label_units' => 'after'
					],
					'result' => '100 after'
				]
			],
			// #11.
			[
				[
					'fields' => [
						'Name' => 'Special symbols and emoji check for units',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => '0',
						'id:secondary_label_decimal_places' => '0',
						'id:primary_label_units_pos' => 'After value',
						'id:secondary_label_units_pos' => 'After value',
						'id:primary_label_units' => '🙂🙃 āēīõšŗ$%^&*()',
						'id:secondary_label_units' => '🙂🙃 āēīõšŗ$%^&*()'
					],
					'result' => '100 🙂🙃 āēīõšŗ$%^&*()'
				]
			],
			// #12.
			[
				[
					'fields' => [
						'Name' => 'User and global macros displayed in units',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Value',
						'id:secondary_label_type' => 'Value',
						'id:primary_label_decimal_places' => '0',
						'id:secondary_label_decimal_places' => '0',
						'id:primary_label_units_pos' => 'After value',
						'id:secondary_label_units_pos' => 'After value',
						'id:primary_label_units' => '{$TEXT} {HOST.NAME}',
						'id:secondary_label_units' => '{$TEXT} {HOST.NAME}'
					],
					'result' => '100 {$TEXT} {HOST.NAME}'
				]
			],
			// #13.
			[
				[
					'fields' => [
						'Name' => 'Only primary label displayed',
						'Item patterns' => 'Display item 1',
						'id:show_2' => false,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => 'Only primary'
					],
					'check_label' => 'svg-honeycomb-label-primary',
					'turned_off_label' => 'svg-honeycomb-label-secondary',
					'result' => 'Only primary'
				]
			],
			// #14.
			[
				[
					'fields' => [
						'Name' => 'Only secondary label displayed',
						'Item patterns' => 'Display item 1',
						'id:show_1' => false,
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => 'Only secondary'
					],
					'check_label' => 'svg-honeycomb-label-secondary',
					'turned_off_label' => 'svg-honeycomb-label-primary',
					'result' => 'Only secondary'
				]
			],
			// #15.
			[
				[
					'fields' => [
						'Name' => 'Colors for value and background',
						'Item patterns' => 'Display item 1',
						'id:primary_label_type' => 'Text',
						'id:secondary_label_type' => 'Text',
						'id:primary_label' => 'COLOR',
						'id:secondary_label' => 'COLOR',
						'xpath:.//input[@id="primary_label_color"]/..' => '66BB6A', // Primary label Color.
						'xpath:.//input[@id="secondary_label_color"]/..' => '80DEEA', // Primary label Color.
						'Background colour' => 'D1C4E9'
					],
					'colors' => [
						'xpath://*[@class="svg-honeycomb-cell"]' => '#D1C4E9',
						'svg-honeycomb-label-primary' => 'rgba(102, 187, 106, 1)',
						'svg-honeycomb-label-secondary' => 'rgba(128, 222, 234, 1)'
					],
					'result' => 'COLOR'
				]
			]
		];
	}

	/**
	 * Check different data display on Honeycomb widget.
	 *
	 * @onBefore prepareUpdateHoneycomb
	 *
	 * @dataProvider getDisplayData
	 */
	public function testDashboardHoneycombWidget_Display($data) {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$disposable_dashboard_id)->waitUntilReady();
		$dashboard = CDashboardElement::find()->waitUntilReady()->one();
		$this->fillWidgetForm($data, 'update', $dashboard);
		$dashboard->save();

		// Check message that dashboard saved.
		$this->assertMessage(TEST_GOOD, 'Dashboard updated');
		$this->page->waitUntilReady();
		$widget = $dashboard->getWidget($data['fields']['Name']);

		// Check that correct value displayed on honeycomb.
		$content = $widget->getContent();
		if (array_key_exists('check_label', $data)) {
			$displayed = $content->query('class', $data['check_label'])->one()->getText();
			$this->assertEquals($displayed, $data['result']);
			$this->assertFalse($content->query('class', $data['turned_off_label'])->exists());
		}
		else {
			foreach (['svg-honeycomb-label-primary', 'svg-honeycomb-label-secondary'] as $selector) {
				$displayed = $content->query('class', $selector)->one()->getText();
				$this->assertEquals($displayed, $data['result']);
			}
		}

		// Check that correct colors displayed.
		if (array_key_exists('colors', $data)) {
			foreach ($data['colors'] as $color_selector => $color) {
				if ($color === '#D1C4E9') {
					$this->assertStringContainsString($color, $this->query($color_selector)->one()->getAttribute('style'));
				}
				else {
					$this->assertEquals($color, $this->query('class', $color_selector)->one()->getCSSValue('color'));
				}
			}
		}
	}

	/**
	 * Test function for assuring that all item types available in Honeycomb widget.
	 */
	public function testDashboardHoneycombWidget_CheckAvailableItems() {
		$this->checkAvailableItems('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid['Dashboard for deleting honeycomb widget'], 'Honeycomb'
		);
	}

	public function getCancelData() {
		return [
			// Cancel update widget.
			[
				[
					'update' => true,
					'save_widget' => true,
					'save_dashboard' => false
				]
			],
			[
				[
					'update' => true,
					'save_widget' => false,
					'save_dashboard' => true
				]
			],
			// Cancel create widget.
			[
				[
					'save_widget' => true,
					'save_dashboard' => false
				]
			],
			[
				[
					'save_widget' => false,
					'save_dashboard' => true
				]
			]
		];
	}

	/**
	 * Check cancel scenarios for Honeycomb widget.
	 *
	 * @dataProvider getCancelData
	 */
	public function testDashboardHoneycombWidget_Cancel($data) {
		self::$old_hash = CDBHelper::getHash(self::SQL);
		$new_name = 'Widget to be cancelled';

		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$dashboardid['Dashboard for canceling honeycomb widget']
		);
		$dashboard = CDashboardElement::find()->one()->edit();
		self::$old_widget_count = $dashboard->getWidgets()->count();

		// Start updating or creating a widget.
		if (CTestArrayHelper::get($data, 'update', false)) {
			$form = $dashboard->getWidget('CancelHoneycomb')->edit();
		}
		else {
			$form = $dashboard->addWidget()->asForm();
			$form->fill(['Type' => CFormElement::RELOADABLE_FILL('Honeycomb')]);
		}

		$form->fill([
			'Name' => $new_name,
			'Advanced configuration' => true,
			'Item patterns' => 'Test_cancel',
			'Refresh interval' => '15 minutes',
			'Host groups' => 'Zabbix servers',
			'Hosts' => 'Host for honeycomb 1',
			'id:primary_label_type' => 'Value',
			'id:secondary_label_type' => 'Text',
			'id:primary_label_decimal_places' => 6,
			'id:secondary_label' => 'some text'
		]);

		// Save or cancel widget.
		if (CTestArrayHelper::get($data, 'save_widget', false)) {
			$form->submit();

			// Check that changes took place on the unsaved dashboard.
			$this->assertTrue($dashboard->getWidget($new_name)->isVisible());
		}
		else {
			$dialog = COverlayDialogElement::find()->one();
			$dialog->close(true);
			$dialog->ensureNotPresent();

			if (CTestArrayHelper::get($data, 'update', false)) {
				foreach (['CancelHoneycomb' => true, $new_name => false] as $name => $valid) {
					$this->assertTrue($dashboard->getWidget($name, $valid)->isValid($valid));
				}
			}

			$this->assertEquals(self::$old_widget_count, $dashboard->getWidgets()->count());
		}
		// Save or cancel dashboard update.
		if (CTestArrayHelper::get($data, 'save_dashboard', false)) {
			$dashboard->save();
		}
		else {
			$dashboard->cancelEditing();
		}
		// Confirm that no changes were made to the widget.
		$this->assertEquals(self::$old_hash, CDBHelper::getHash(self::SQL));
	}

	/**
	 * Check different comb compositions for Honeycomb widget.
	 */
	public function testDashboardHoneycombWidget_Screenshots() {
		$this->page->login();

		for ($i = 1; $i <= 5; $i++) {
			$this->page->open('zabbix.php?action=dashboard.view&dashboardid='.
					self::$dashboardid['Dashboard for Honeycomb screenshot'].'&page='.$i)->waitUntilReady();

			$element = CDashboardElement::find()->one()->getWidget('Honeycomb');
			$this->assertScreenshot($element, 'honeycomb_'.$i);
		}
	}

	/**
	 * Creates the base widget used for the update scenario.
	 */
	public function prepareFilteringHoneycomb() {
		$providedData = $this->getProvidedData();
		$data = reset($providedData);

		// Create a dashboard with the widget for updating.
		$response = CDataHelper::call('dashboard.create', [
			[
				'name' => 'Dashboard for filtering '.md5(serialize($data)),
				'auto_start' => 0,
				'pages' => [
					[
						'widgets' => [
							[
								'type' => 'honeycomb',
								'name' => 'UpdateHoneycomb',
								'x' => 0,
								'y' => 0,
								'width' => 30,
								'height' => 7,
								'fields' => [
									[
										'type' => 0,
										'name' => 'show.0',
										'value' => 1
									],
									[
										'type' => 1,
										'name' => 'primary_label',
										'value' => '{ITEM.NAME}'
									],
									[
										'type' => 1,
										'name' => 'items.0',
										'value' => 'test'
									],
									[
										'type' => 1,
										'name' => 'reference',
										'value' => 'BUBUR'
									]
								]
							]
						]
					]
				]
			]
		]);
		self::$disposable_dashboard_id = $response['dashboardids'][0];
	}

	public static function getFilteringData() {
		return [
			// #0 Filter by 3 items.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'filtered_items' => ['Item tag 1', 'Item tag 2', 'Host tag item']
				]
			],
			// #1 Filter by Host group.
			[
				[
					'fields' => [
						'Host groups' => 'Items with tags',
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'filtered_items' => ['Item tag 1', 'Item tag 2']
				]
			],
			// #2 Filter by Host.
			[
				[
					'fields' => [
						'Hosts' => 'Host with tags',
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'filtered_items' => ['Host tag item']
				]
			],
			// #3 Show hosts in maintenance.
			[
				[
					'fields' => [
						'Show hosts in maintenance' => true,
						'Item patterns' => ['Item tag 1', 'Host tag item', 'Maintenance item']
					],
					'filtered_items' => ['Maintenance item', 'Item tag 1', 'Host tag item']
				]
			],
			// #4 Don't show hosts in maintenance.
			[
				[
					'fields' => [
						'Show hosts in maintenance' => false,
						'Item patterns' => ['Item tag 1', 'Host tag item', 'Maintenance item']
					],
					'filtered_items' => ['Item tag 1', 'Host tag item']
				]
			],
			// #5 Filter by Host exists tag.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'tags' => [
						'host_tags' => [
							['name' => 'host_tag_1', 'operator' => 'Exists']
						]
					],
					'filtered_items' => ['Host tag item']
				]
			],
			// #6 Filter by Host doesn't exist tag.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'tags' => [
						'host_tags' => [
							['name' => 'host_tag_1', 'operator' => 'Does not exist']
						]
					],
					'filtered_items' => ['Item tag 1', 'Item tag 2']
				]
			],
			// #7 Filter by Items exists tag.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Exists']
						]
					],
					'filtered_items' => ['Item tag 1']
				]
			],
			// #8 Filter by Items doesn't exist tag.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Does not exist']
						]
					],
					'filtered_items' => ['Item tag 2', 'Host tag item']
				]
			],
			// #9 Filter by Items and Host exists tag.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item']
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Exists']
						],
						'host_tags' => [
							['name' => 'host_tag_1', 'operator' => 'Exists']
						]
					],
					'filtered_items' => ['No data']
				]
			],
			// #10 Filter by Items and Host doesn't exist tag.
			[
				[
					'fields' => [
						'Show hosts in maintenance' => True,
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Host tag item', 'Maintenance item']
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Does not exist']
						],
						'host_tags' => [
							['name' => 'host_tag_1', 'operator' => 'Does not exist']
						]
					],
					'filtered_items' => ['Maintenance item', 'Item tag 2']
				]
			],
			// #11 Item tag Or with operators Contain.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Item tag 3', 'Item tag 4', 'Item tag 5'],
						'Item tags' => 'Or'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Contains', 'value' => 'item_val_1'],
							['name' => 'item_tag_3', 'operator' => 'Contains', 'value' => 'item_val_3']
						]
					],
					'filtered_items' => ['Item tag 1', 'Item tag 3', 'Item tag 4']
				]
			],
			// #12 Item tag And/Or with operators Contain.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Item tag 3', 'Item tag 4', 'Item tag 5'],
						'Item tags' => 'And/Or'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Contains', 'value' => 'item_val_1'],
							['name' => 'item_tag_3', 'operator' => 'Contains', 'value' => 'item_val_3']
						]
					],
					'filtered_items' => ['No data']
				]
			],
			// #13 Item tag Or with operators Exists.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Item tag 3', 'Item tag 4', 'Item tag 5'],
						'Item tags' => 'Or'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Exists'],
							['name' => 'item_tag_3', 'operator' => 'Exists']
						]
					],
					'filtered_items' => ['Item tag 1', 'Item tag 3', 'Item tag 4', 'Item tag 5']
				]
			],
			// #14 Item tag And/Or with operators Exists.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Item tag 3', 'Item tag 4', 'Item tag 5'],
						'Item tags' => 'And/Or'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_1', 'operator' => 'Exists'],
							['name' => 'item_tag_3', 'operator' => 'Exists']
						]
					],
					'filtered_items' => ['No data']
				]
			],
			// #15 Item tag Or with operators Does not exist and Contains.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Item tag 3', 'Item tag 4', 'Item tag 5'],
						'Item tags' => 'Or'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_2', 'operator' => 'Contains', 'value' => 'item_val_2'],
							['name' => 'item_tag_3', 'operator' => 'Does not exist']
						]
					],
					'filtered_items' => ['Item tag 1', 'Item tag 2', 'Item tag 4', 'Item tag 5']
				]
			],
			// #16 Item tag And/Or with operators Does not exist and Contains.
			[
				[
					'fields' => [
						'Item patterns' => ['Item tag 1', 'Item tag 2', 'Item tag 3', 'Item tag 4', 'Item tag 5'],
						'Item tags' => 'And/Or'
					],
					'tags' => [
						'item_tags' => [
							['name' => 'item_tag_2', 'operator' => 'Contains', 'value' => 'item_val_2'],
							['name' => 'item_tag_3', 'operator' => 'Does not exist']
						]
					],
					'filtered_items' => ['Item tag 2']
				]
			]
		];
	}

	/**
	 * Filter honeycomb and check that correct item comb visible on widget.
	 *
	 * @dataProvider getFilteringData
	 *
	 * @onBefore prepareFilteringHoneycomb
	 */
	public function testDashboardHoneycombWidget_CheckFiltering($data) {
		$this->page->login()->open('zabbix.php?action=dashboard.view&dashboardid='.
				self::$disposable_dashboard_id)->waitUntilReady();
		$dashboard = CDashboardElement::find()->waitUntilReady()->one();
		$this->fillWidgetForm($data, 'update', $dashboard);
		$dashboard->save();

		// Check message that dashboard saved.
		$this->page->waitUntilReady();
		$this->assertMessage(TEST_GOOD, 'Dashboard updated');

		// Check that correct combs displayed on honeycomb.
		$filtered = $dashboard->getWidget('UpdateHoneycomb')->getContent()->query('class', 'svg-honeycomb-content')
				->all()->asText();
		$this->assertEquals($data['filtered_items'], $filtered);
	}

	public static function getMacroFunctions() {
		return [
			'Incorrectly added parameter for non-argument macro functions' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{{ITEM.NAME}.btoa(\)}, {'.self::USER_MACRO.'.htmldecode(test)}, '.
							'{'.self::USER_MACRO.'.htmlencode(test)}, {{ITEM.NAME}.lowercase([test])}, '.
							'{{ITEM.NAME}.uppercase([test])}, {{ITEM.NAME}.urldecode([test])}, '.
							'{'.self::USER_SECRET_MACRO.'.urlencode(\/)}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{{ITEM.NAME}.btoa(\)}, {'.self::USER_MACRO.'.htmldecode(test)}, '.
							'{'.self::USER_MACRO.'.htmlencode(test)}, {{ITEM.NAME}.lowercase([test])}, '.
							'{{ITEM.NAME}.uppercase([test])}, {{ITEM.NAME}.urldecode([test])}, '.
							'{'.self::USER_SECRET_MACRO.'.urlencode(\/)}'
					],
					'result' => [
						'primary' => '*UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*',
						'secondary' => '*UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*'
					]
				]
			],
			'Secret macro value is not exposed when using macro functions' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::USER_SECRET_MACRO.'.btoa()}, {'.self::USER_SECRET_MACRO.'.htmldecode()}, '.
							'{'.self::USER_SECRET_MACRO.'.htmlencode()}, {'.self::USER_SECRET_MACRO.'.lowercase()}, '.
							'{'.self::USER_SECRET_MACRO.'.uppercase()}, {'.self::USER_SECRET_MACRO.'.regrepl(a, b)}, '.
							'{'.self::USER_SECRET_MACRO.'.tr(a-z, b)}, {'.self::USER_SECRET_MACRO.'.urldecode()}, '.
							'{'.self::USER_SECRET_MACRO.'.urlencode()}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::USER_SECRET_MACRO.'.btoa()}, {'.self::USER_SECRET_MACRO.'.htmldecode()}, '.
							'{'.self::USER_SECRET_MACRO.'.htmlencode()}, {'.self::USER_SECRET_MACRO.'.lowercase()}, '.
							'{'.self::USER_SECRET_MACRO.'.uppercase()}, {'.self::USER_SECRET_MACRO.'.regrepl(a, b)}, '.
							'{'.self::USER_SECRET_MACRO.'.tr(a-z, b)}, {'.self::USER_SECRET_MACRO.'.urldecode()}, '.
							'{'.self::USER_SECRET_MACRO.'.urlencode()}'
					],
					'result' => [
						'primary' => 'KioqKioq, ******, ******, ******, ******, ******, ******, ******, %2A%2A%2A%2A%2A%2A',
						'secondary' => 'KioqKioq, ******, ******, ******, ******, ******, ******, ******, %2A%2A%2A%2A%2A%2A'
					]
				]
			],
			'Built-in macros with non-argument macro functions' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{{ITEM.NAME}.btoa()}, {{ITEM.NAME}.htmldecode()}, {{ITEM.NAME}.htmlencode()}, '.
							'{{ITEM.NAME}.lowercase()}, {{ITEM.NAME}.uppercase()}, {{ITEM.NAME}.urlencode()}, '.
							'{{ITEM.NAME}.urldecode()}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{{ITEM.NAME}.btoa()}, {{ITEM.NAME}.htmldecode()}, {{ITEM.NAME}.htmlencode()}, '.
							'{{ITEM.NAME}.lowercase()}, {{ITEM.NAME}.uppercase()}, {{ITEM.NAME}.urlencode()}, '.
							'{{ITEM.NAME}.urldecode()}'
					],
					'result' => [
						'primary' => 'RGlzcGxheSBpdGVtIDU=, Display item 5, Display item 5, display item 5, '.
							'DISPLAY ITEM 5, Display%20item%205, Display item 5',
						'secondary' => 'RGlzcGxheSBpdGVtIDU=, Display item 5, Display item 5, display item 5, '.
							'DISPLAY ITEM 5, Display%20item%205, Display item 5'
					]
				]
			],
			'User macros with btoa(), htmlencode(), htmldecode() macro functions' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::USER_MACRO.'.btoa()}, {'.self::MACRO_HTML_ENCODE.'.htmlencode()}, '.
							'{'.self::MACRO_HTML_DECODE.'.htmldecode()}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::USER_MACRO.'.btoa()}, {'.self::MACRO_HTML_ENCODE.'.htmlencode()}, '.
							'{'.self::MACRO_HTML_DECODE.'.htmldecode()}'
					],
					'result' => [
						'primary' => base64_encode(self::USER_MACRO_VALUE).', '.self::MACRO_HTML_DECODE_VALUE.', '.
							self::MACRO_HTML_ENCODE_VALUE,
						'secondary' => base64_encode(self::USER_MACRO_VALUE).', '.self::MACRO_HTML_DECODE_VALUE.', '.
							self::MACRO_HTML_ENCODE_VALUE
					]
				]
			],
			'User macros with urlencode(), urldecode(), uppercase(), lowercase() macro functions' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::MACRO_URL_ENCODE.'.urlencode()}, '.
							'{'.self::MACRO_URL_DECODE.'.urldecode()}, {'.self::USER_MACRO.'.uppercase()}, '.
							'{'.self::USER_MACRO.'.lowercase()}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::MACRO_URL_ENCODE.'.urlencode()}, '.
							'{'.self::MACRO_URL_DECODE.'.urldecode()}, {'.self::USER_MACRO.'.uppercase()}, '.
							'{'.self::USER_MACRO.'.lowercase()}'
					],
					'result' => [
						'primary' => self::MACRO_URL_DECODE_VALUE.', '.self::MACRO_URL_ENCODE_VALUE.
							', MACRO FUNCTION TEST 12345, macro function test 12345',
						'secondary' => self::MACRO_URL_DECODE_VALUE.', '.self::MACRO_URL_ENCODE_VALUE.
							', MACRO FUNCTION TEST 12345, macro function test 12345'
					]
				]
			],
			'Incorrectly used parameters in regrepl(), tr(), regsub(), iregsub() macro functions' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::USER_MACRO.'.regrepl()}, {'.self::MACRO_CHAR.'.regrepl([a])}, '.
							'{'.self::USER_MACRO.'.tr()}, {'.self::USER_MACRO.'.tr(z-a,Z-A)}, {'.self::USER_MACRO.'.tr(1,2,3)}'.
							', {'.self::USER_MACRO.'.regsub()}, {'.self::USER_MACRO.'.iregsub()}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::USER_MACRO.'.regrepl()}, {'.self::MACRO_CHAR.'.regrepl([a])}, '.
							'{'.self::USER_MACRO.'.tr()}, {'.self::USER_MACRO.'.tr(z-a,Z-A)}, {'.self::USER_MACRO.'.tr(1,2,3)}'.
							', {'.self::USER_MACRO.'.regsub()}, {'.self::USER_MACRO.'.iregsub()}'
					],
					'result' => [
						'primary' => '*UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*',
						'secondary' => '*UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*, *UNKNOWN*'
					]
				]
			],
			'Regrepl function - multibyte characters and case sensitive check' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::USER_MACRO.'.regrepl([[:digit:]], /, [A-Z], \)}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::MACRO_CHAR.'.regrepl(🌴, 🌝, [а-я], Q, \d, 🌞)}'
					],
					'result' => [
						'primary' => '\acro function \est /////',
						'secondary' => '🌞🌞🌞 ЙQQQQЖŽzŠsšĒĀīī🌝 ₰₰₰'
					]
				]
			],
			'Regrepl function with big amount of processed data' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::USER_MACRO.''.
							'.regrepl(1{0}, test, 1{0}, test, 1{0},test, 1{0}, test, 1{0}, test, 1{0}, test)}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::USER_MACRO.''.
							'.regrepl(1{0}, test, 1{0}, test, 1{0},test, 1{0}, test, 1{0}, test, 1{0}, test)}'
					],
					'result' => [
						'primary' => '*UNKNOWN*',
						'secondary' => '*UNKNOWN*'
					]
				]
			],
			'Macro functions tr(), uppercase(), lowercase() with non-ascii characters' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::MACRO_CHAR.'.tr(0-9, Ī)}, {'.self::MACRO_CHAR.'.lowercase()}, '.
							'{'.self::MACRO_CHAR.'.uppercase()}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::MACRO_CHAR.'.tr(0-9, Ī)}, {'.self::MACRO_CHAR.'.lowercase()}, '.
							'{'.self::MACRO_CHAR.'.uppercase()}'
					],
					'result' => [
						'primary' => '??? ЙщфхжЖŽzŠsšĒĀīī🌴 ₰₰₰, 000 ЙщфхжЖŽzŠsšĒĀīī🌴 ₰₰₰, 000 ЙщфхжЖŽZŠSšĒĀīī🌴 ₰₰₰',
						'secondary' => '??? ЙщфхжЖŽzŠsšĒĀīī🌴 ₰₰₰, 000 ЙщфхжЖŽzŠsšĒĀīī🌴 ₰₰₰, 000 ЙщфхжЖŽZŠSšĒĀīī🌴 ₰₰₰'
					]
				]
			],
			'Macro function tr() - use of escaping and range' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::MACRO_URL_ENCODE.'.tr("\/","\"")}, {'.self::MACRO_CHAR.'.tr(0-9abcA-L,*)}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::MACRO_URL_ENCODE.'.tr("\/","\"")}, {'.self::MACRO_CHAR.'.tr(0-9abcA-L,*)}'
					],
					'result' => [
						'primary' => 'h:""test.com"macro?functions=urlencode&urld=a🎸, *** ЙщфхжЖŽzŠsšĒĀīī🌴 ₰₰₰',
						'secondary' => 'h:""test.com"macro?functions=urlencode&urld=a🎸, *** ЙщфхжЖŽzŠsšĒĀīī🌴 ₰₰₰'
					]
				]
			],
			'Macro functions regsub(), iregsub() - successful scenarios' => [
				[
					'fields' => [
						'Advanced configuration' => true,
						'id:primary_label_type' => 'Text',
						'id:primary_label' => '{'.self::USER_MACRO.'.regsub(^[0-9]+, Problem)}, '.
							'{'.self::USER_MACRO.'.iregsub(^[0-9]+, Problem)}, {{ITEM.NAME}.regsub(^[0-9]+, Problem)}, '.
							'{{ITEM.NAME}.iregsub(^[0-9]+, Problem)}, {'.self::USER_SECRET_MACRO.'.regsub(^[0-9]+, Problem)}, '.
							'{'.self::USER_SECRET_MACRO.'.iregsub(^[0-9]+, Problem)}',
						'id:secondary_label_type' => 'Text',
						'id:secondary_label' => '{'.self::USER_MACRO.'.regsub(^[0-9]+, Problem)}, '.
							'{'.self::USER_MACRO.'.iregsub(^[0-9]+, Problem)}, {{ITEM.NAME}.regsub(^[0-9]+, Problem)}, '.
							'{{ITEM.NAME}.iregsub(^[0-9]+, Problem)}, {'.self::USER_SECRET_MACRO.'.regsub(^[0-9]+, Problem)}, '.
							'{'.self::USER_SECRET_MACRO.'.iregsub(^[0-9]+, Problem)}'
					],
					'result' => [
						'primary' => 'Problem, Problem, Problem, Problem, Problem, Problem',
						'secondary' => 'Problem, Problem, Problem, Problem, Problem, Problem'
					]
				]
			]
			// TODO: Uncomment and check the test case, after ZBX-25420 fix.
//			'Macro functions regsub(), iregsub() -  empty value in case of no match' => [
//				[
//					'fields' => [
//						'Advanced configuration' => true,
//						'id:primary_label_type' => 'Text',
//						'id:primary_label' => '{'.self::USER_MACRO.'.regsub(0, Problem)}, '.
//							'{'.self::USER_MACRO.'.iregsub(0, Problem)}, {{ITEM.NAME}.regsub(0, Problem)}, '.
//							'{{ITEM.NAME}.iregsub(0, Problem)}, {'.self::USER_SECRET_MACRO.'.regsub(0, Problem)}, '.
//							'{'.self::USER_SECRET_MACRO.'.iregsub(0, Problem)}, ',
//						'id:secondary_label_type' => 'Text',
//						'id:secondary_label' => '{'.self::USER_MACRO.'.regsub(0, Problem)}, '.
//							'{'.self::USER_MACRO.'.iregsub(0, Problem)}, {{ITEM.NAME}.regsub(0, Problem)}, '.
//							'{{ITEM.NAME}.iregsub(0, Problem)}, {'.self::USER_SECRET_MACRO.'.regsub(0, Problem)}, '.
//							'{'.self::USER_SECRET_MACRO.'.iregsub(0, Problem)}, '
//					],
//					'result' => [
//						'primary' => ', , , , ,',
//						'secondary' => ', , , , ,'
//					]
//				]
//			]
		];
	}

	/**
	 * @dataProvider getMacroFunctions
	 */
	public function testDashboardHoneycombWidget_CheckMacroFunctions($data) {
		$this->setWidgetConfiguration(self::$dashboardid[self::DASHBOARD_FOR_MACRO_FUNCTIONS],
				self::WIDGET_FOR_MACRO_FUNCTIONS, $data['fields']
		);
		CDashboardElement::find()->one()->save()->waitUntilReady();

		// Check the resolution of macrofunction.
		$this->assertEquals($data['result']['primary'],
				$this->query('xpath://div[@class="svg-honeycomb-label svg-honeycomb-label-primary"]')->one()->getText()
		);
		$this->assertEquals($data['result']['secondary'],
				$this->query('xpath://div[@class="svg-honeycomb-label svg-honeycomb-label-secondary"]')->one()->getText()
		);
	}

	/**
	 * Get threshold table element with mapping set.
	 *
	 * @return CMultifieldTable
	 */
	protected function getTreshholdTable() {
		return $this->query('id:thresholds-table')->asMultifieldTable([
			'mapping' => [
				'' => [
					'name' => 'color',
					'selector' => 'class:color-picker',
					'class' => 'CColorPickerElement'
				],
				'Threshold' => [
					'name' => 'threshold',
					'selector' => 'xpath:./input',
					'class' => 'CElement'
				]
			]
		])->waitUntilVisible()->one();
	}

	/**
	 * Create or update Honeycomb widget.
	 *
	 * @param array             $data         data provider
	 * @param string            $action       create/update honeycomb widget
	 * @param CDashboardElement $dashboard    given dashboard
	 */
	protected function fillWidgetForm($data, $action, $dashboard) {
		$form = ($action === 'create')
			? $dashboard->edit()->addWidget()->asForm()
			: $dashboard->getWidget('UpdateHoneycomb')->edit();

		$form->fill(['Type' => CFormElement::RELOADABLE_FILL('Honeycomb')]);
		$form->fill(['Advanced configuration' => true]);
		$this->query('id:lbl_bg_color')->one()->waitUntilVisible();

		// Fill Thresholds values.
		if (array_key_exists('thresholds', $data)) {
			$this->getTreshholdTable()->fill($data['thresholds']);
			unset($data['thresholds']);
		}

		if (array_key_exists('tags', $data)) {
			$this->addOrCheckTags($data['tags'], false);
		}

		$form->fill($data['fields']);
		$form->submit();
	}

	/**
	 * Check created or updated Honeycomb widget.
	 *
	 * @param array             $data         data provider
	 * @param string            $action       create/update honeycomb widget
	 * @param CDashboardElement $dashboard    given dashboard
	 */
	protected function checkWidgetForm($data, $action, $dashboard) {
		if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
			$this->assertMessage(TEST_BAD, null, $data['error_message']);
			COverlayDialogElement::find()->one()->close();
			$dashboard->save();
			$this->assertMessage(TEST_GOOD, 'Dashboard updated');

			// Compare old hash and new one.
			$this->assertEquals(self::$old_hash, CDBHelper::getHash(self::SQL));
		}
		else {
			// Make sure that the widget is present before saving the dashboard.
			$header = (array_key_exists('Name', $data['fields']))
				? (($data['fields']['Name'] === '') ? 'Honeycomb' : $data['fields']['Name'])
				: 'Honeycomb';

			$dashboard->getWidget($header);
			$dashboard->save();

			// Check message that dashboard saved.
			$this->assertMessage(TEST_GOOD, 'Dashboard updated');

			// Check widget amount that it is added.
			$this->assertEquals(self::$old_widget_count + (($action === 'create') ? 1 : 0), $dashboard->getWidgets()->count());

			$form = $dashboard->getWidget($header)->edit()->asForm();
			$form->fill(['Advanced configuration' => true]);
			$this->query('id:lbl_bg_color')->one()->waitUntilVisible();

			if (array_key_exists('tags', $data)) {
				$this->addOrCheckTags($data['tags']);
			}

			// Check Thresholds values.
			if (array_key_exists('thresholds', $data)) {
				$this->getTreshholdTable()->checkValue($data['thresholds']);
			}

			$form->checkValue($data['fields']);
			COverlayDialogElement::find()->one()->close();
			$dashboard->save();
			$this->assertMessage(TEST_GOOD, 'Dashboard updated');
		}
	}

	/**
	 * Add or Check tags in Honeycomb widget.
	 *
	 * @param array   $tags     given tags
	 * @param boolean $check    check tags' values after creation or not
	 */
	protected function addOrCheckTags($tags, $check = true) {
		foreach ($tags as $tag => $values) {
			$this->setTagSelector(($tag === 'item_tags') ? 'id:tags_table_item_tags' : 'id:tags_table_host_tags');

			if ($check) {
				$this->assertTags($values);
			}
			else {
				$this->setTags($values);
			}
		}
	}
}