<?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__).'/../../include/CWebTest.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';

/**
 * @backup scripts
 *
 * @onBefore prepareScriptData
 */
class testFormAlertsScripts extends CWebTest {

	/**
	 * Id of scripts that created for future cloning.
	 *
	 * @var integer
	 */
	protected static $clone_scriptids;

	/**
	 * Id of scripts.
	 *
	 * @var array
	 */
	protected static $ids;

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

	/**
	 * Function used to create scripts.
	 */
	public function prepareScriptData() {
		$response = CDataHelper::call('script.create', [
			[
				'name' => 'Script for Clone',
				'type' => ZBX_SCRIPT_TYPE_WEBHOOK,
				'scope' => ZBX_SCRIPT_SCOPE_HOST,
				'command' => 'test',
				'parameters' => [
					[
						'name' => 'name1',
						'value' => 'value1'
					],
					[
						'name' => 'name2',
						'value' => 'value2'
					]
				],
				'description' => 'clone description'
			],
			[
				'name' => 'SSH_api_clone_1',
				'type' => ZBX_SCRIPT_TYPE_SSH,
				'scope' => ZBX_SCRIPT_SCOPE_ACTION,
				'username' => 'SSH_username',
				'password' => 'SSH_password',
				'command' => 'test',
				'port' => '80'
			],
			[
				'name' => 'SSH_api_clone_2',
				'type' => ZBX_SCRIPT_TYPE_SSH,
				'scope' => ZBX_SCRIPT_SCOPE_ACTION,
				'authtype' => ITEM_AUTHTYPE_PUBLICKEY,
				'username' => 'SSH_username',
				'privatekey' => 'private_key',
				'publickey' => 'public_key',
				'command' => 'test'
			],
			[
				'name' => 'TELNET_api_clone',
				'type' => ZBX_SCRIPT_TYPE_TELNET,
				'scope' => ZBX_SCRIPT_SCOPE_ACTION,
				'username' => 'TELNET_username',
				'password' => 'TELNET_password',
				'command' => 'test'
			],
			[
				'name' => 'type URL, manual host event for clone',
				'type' => ZBX_SCRIPT_TYPE_URL,
				'scope' => ZBX_SCRIPT_SCOPE_HOST,
				'new_window' => ZBX_SCRIPT_URL_NEW_WINDOW_NO,
				'menu_path' => 'menu/path',
				'url' => 'sysmaps.php'
			],
			[
				'name' => 'type URL, manual action event for clone',
				'type' => ZBX_SCRIPT_TYPE_URL,
				'scope' => ZBX_SCRIPT_SCOPE_EVENT,
				'url' => 'zabbix.com'
			]
		]);
		$this->assertArrayHasKey('scriptids', $response);
		self::$clone_scriptids = $response['scriptids'];

		$scripts = CDataHelper::call('script.create', [
			[
				'name' => 'Script for Update',
				'type' => ZBX_SCRIPT_TYPE_WEBHOOK,
				'scope' => ZBX_SCRIPT_SCOPE_HOST,
				'command' => 'test',
				'parameters' => [
					[
						'name' => 'update_name',
						'value' => 'update_value'
					],
					[
						'name' => 'update_name2',
						'value' => 'update_value2'
					]
				],
				'description' => 'update description'
			],
			[
				'name' => 'Script for Delete',
				'type' => ZBX_SCRIPT_TYPE_WEBHOOK,
				'scope' => ZBX_SCRIPT_SCOPE_HOST,
				'command' => 'test',
				'description' => 'delete description'
			],
			[
				'name' => 'URI schemes',
				'type' => ZBX_SCRIPT_TYPE_URL,
				'scope' => ZBX_SCRIPT_SCOPE_HOST,
				'url' => 'sysmaps.php'
			]
		]);
		$this->assertArrayHasKey('scriptids', $scripts);
		self::$ids = CDataHelper::getIds('name');
	}

	/**
	 * Test data for Scripts form.
	 */
	public function getScriptsData() {
		return [
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "timeout": value must be one of 1-60.',
					'fields' => [
						'Name' => 'Timeout test 0',
						'Script' => 'java script',
						'Timeout' => '0'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "timeout": value must be one of 1-60.',
					'fields' => [
						'Name' => 'Timeout test 1h',
						'Script' => 'java script',
						'Timeout' => '1h'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "timeout": value must be one of 1-60.',
					'fields' => [
						'Name' => 'Timeout test 70',
						'Script' => 'java script',
						'Timeout' => '70s'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "timeout": a time unit is expected.',
					'fields' => [
						'Name' => 'Timeout test -1',
						'Script' => 'java script',
						'Timeout' => '-1'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "timeout": a time unit is expected.',
					'fields' => [
						'Name' => 'Timeout test character',
						'Script' => 'java script',
						'Timeout' => 'char'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/parameters/1/name": cannot be empty.',
					'fields' => [
						'Name' => 'Test empty parameters',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => '',
							'Value' => ''
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => '',
							'Value' => ''
						]
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/parameters/2": value (name)=(Param1) already exists.',
					'fields' => [
						'Name' => 'Test empty parameter names',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => 'Param1',
							'Value' => 'Value1'
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => 'Param1',
							'Value' => 'Value'
						]
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/parameters/1/name": cannot be empty.',
					'fields' => [
						'Name' => 'Test trailing spaces',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => '   ',
							'Value' => '   '
						]
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/command": cannot be empty.',
					'fields' => [
						'Name' => 'Webhook Empty script',
						'Script' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "name": cannot be empty.',
					'fields' => [
						'Name' => '',
						'Script' => 'Webhook: empty name'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "name": cannot be empty.',
					'fields' => [
						'Name' => '',
						'Type' => 'Script',
						'Commands' => 'Script empty name'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/command": cannot be empty.',
					'fields' => [
						'Name' => 'Script empty command',
						'Type' => 'Script',
						'Commands' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "name": cannot be empty.',
					'fields' => [
						'Name' => '',
						'Type' => 'IPMI',
						'Command' => 'IPMI empty name'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/command": cannot be empty.',
					'fields' => [
						'Name' => 'IPMI empty command',
						'Type' => 'IPMI',
						'Command' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "name": cannot be empty.',
					'fields' => [
						'Name' => '',
						'Type' => 'SSH',
						'Commands' => 'SSH empty name'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/command": cannot be empty.',
					'fields' => [
						'Name' => 'SSH empty command',
						'Type' => 'SSH',
						'Commands' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/username": cannot be empty.',
					'fields' => [
						'Name' => 'SSH empty username',
						'Type' => 'SSH',
						'Commands' => 'SSH empty username',
						'Username' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "name": cannot be empty.',
					'fields' => [
						'Name' => '',
						'Type' => 'Telnet',
						'Commands' => 'Telnet empty name'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/command": cannot be empty.',
					'fields' => [
						'Name' => 'Telnet empty command',
						'Type' => 'Telnet',
						'Commands' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/username": cannot be empty.',
					'fields' => [
						'Name' => 'Telnet empty username',
						'Type' => 'Telnet',
						'Commands' => 'Telnet empty username',
						'Username' => ''
					]
				]
			],
			// URL type.
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/url": cannot be empty.',
					'fields' => [
						'Name' => 'Url empty for host action',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => ''
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/url": cannot be empty.',
					'fields' => [
						'Name' => 'Url empty for event action',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => '     '
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/url": unacceptable URL.',
					'fields' => [
						'Name' => 'invalid uri schema',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => 'htt://zabbix.com'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/menu_path": directory cannot be empty.',
					'fields' => [
						'Name' => 'invalid menu path',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => 'zabbix.com',
						'Menu path' => '/ /'
					]
				]
			],
			// User input fields validation.
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_prompt": cannot be empty.',
					'fields' => [
						'Name' => 'invalid input prompt',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => 'http://zabbix.com',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input validation rule' => '^$' // should match an empty string.
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_prompt": cannot be empty.',
					'fields' => [
						'Name' => 'invalid input prompt',
						'Scope' => 'Manual event action',
						'Type' => 'Webhook',
						'Script' => 'ping localhost',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => ' ',
						'Input validation rule' => '^$' // should match an empty string.
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_default_value": input does not match the provided pattern: ^$.',
					'fields' => [
						'Name' => 'invalid default input string - not match empty string',
						'Scope' => 'Manual event action',
						'Type' => 'Script',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'test empty input',
						'Default input string' => 'test',
						'Input validation rule' => '^$' // should match an empty string.
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_default_value": input does not match the provided pattern: ^.*@.*\..*$.',
					'fields' => [
						'Name' => 'invalid default input string - not match an email input',
						'Scope' => 'Manual host action',
						'Type' => 'SSH',
						'Username' => 'localhost',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'test email input',
						'Default input string' => 'a$a.lv',
						'Input validation rule' => '^.*@.*\..*$' // should match an email.
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_validator": cannot be empty.',
					'fields' => [
						'Name' => 'invalid input validation rule',
						'Scope' => 'Manual event action',
						'Type' => 'Telnet',
						'Username' => 'localhost',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'test'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_validator": cannot be empty.',
					'fields' => [
						'Name' => 'invalid input validation rule',
						'Scope' => 'Manual host action',
						'Type' => 'IPMI',
						'Command' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'test',
						'Input validation rule' => '  '
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_validator": cannot be empty.',
					'fields' => [
						'Name' => 'invalid dropdown options',
						'Scope' => 'Manual host action',
						'Type' => 'Telnet',
						'Username' => 'test',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input type' => 'Dropdown',
						'Input prompt' => 'test'
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_validator": cannot be empty.',
					'fields' => [
						'Name' => 'invalid dropdown options',
						'Scope' => 'Manual event action',
						'Type' => 'SSH',
						'Username' => 'test',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input type' => 'Dropdown',
						'Input prompt' => 'test',
						'Dropdown options' => ' '
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_validator": values must be unique.',
					'fields' => [
						'Name' => 'invalid dropdown options',
						'Scope' => 'Manual host action',
						'Type' => 'Webhook',
						'Script' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input type' => 'Dropdown',
						'Input prompt' => 'test',
						'Dropdown options' => ','
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Invalid parameter "/1/manualinput_validator": values must be unique.',
					'fields' => [
						'Name' => 'invalid dropdown options',
						'Scope' => 'Manual event action',
						'Type' => 'Script',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input type' => 'Dropdown',
						'Input prompt' => 'test',
						'Dropdown options' => 'a,,b,,c'
					]
				]
			],
			// Confirmation text validation.
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "confirmation": cannot be empty.',
					'fields' => [
						'Name' => 'invalid confirmation text',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => 'https://zabbix.com/',
						'Advanced configuration' => true,
						'Enable confirmation' => true
					]
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'details' => 'Incorrect value for field "confirmation": cannot be empty.',
					'fields' => [
						'Name' => 'invalid confirmation text',
						'Scope' => 'Manual event action',
						'Type' => 'Script',
						'Commands' => 'ping 127.0.0.1',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => ''
					]
				]
			],
			// Webhook.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Minimal script',
						'Script' => 'java script'
					]
				]
			],
			// Remove trailing spaces.
			[
				[
					'expected' => TEST_GOOD,
					'trim' => true,
					'fields' => [
						'Name' => 'Test trailing spaces',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => 'name',
							'Value' => '   trimmed    value    '
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => '   trimmed     name    ',
							'Value' => 'value'
						]
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max webhook',
						'Scope' => 'Manual host action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script',
						'Timeout' => '60s',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Zabbix servers',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => 'Execute script?'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => 'host',
							'Value' => '{HOST.HOST}'
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => 'var',
							'Value' => 'Value'
						]
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max webhook 2',
						'Scope' => 'Action operation',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script',
						'Timeout' => '60s',
						'Description' => 'Test description',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Zabbix servers'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => 'host',
							'Value' => '{HOST.HOST}'
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => 'var',
							'Value' => 'Value'
						]
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max webhook 3',
						'Scope' => 'Manual event action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script',
						'Timeout' => '60s',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Zabbix servers',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => 'Execute script?'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => 'host',
							'Value' => '{HOST.HOST}'
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => 'var',
							'Value' => 'Value'
						]
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Test parameters',
						'Type' => 'Webhook',
						'Script' => 'Webhook Script',
						'Timeout' => '1s'
					],
					'Parameters' => [
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 0,
							'Name' => '!@#$%^&*()_+<>,.\/',
							'Value' => '!@#$%^&*()_+<>,.\/'
						],
						[
							'action' => USER_ACTION_UPDATE,
							'index' => 1,
							'Name' => str_repeat('n', 255),
							'Value' => str_repeat('v', 2048)
						],
						[
							'Name' => '{$MACRO:A}',
							'Value' => '{$MACRO:A}'
						],
						[
							'Name' => '{$USERMACRO}',
							'Value' => ''
						],
						[
							'Name' => '{HOST.HOST}'
						],
						[
							'Name' => 'Имя',
							'Value' => 'Значение'
						]
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Webhook false confirmation',
						'Script' => 'webhook',
						'Script' => 'java script',
						'Enable confirmation' => false
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Timeout test 1',
						'Script' => 'java script',
						'Timeout' => '1'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Timeout test 60',
						'Script' => 'java script',
						'Timeout' => '60'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Timeout test 1m',
						'Script' => 'java script',
						'Timeout' => '1m'
					]
				]
			],
			// Script.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max script',
						'Scope' => 'Manual host action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'Script',
						'Execute on' => 'Zabbix proxy or server',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max script 2',
						'Scope' => 'Action operation',
						'Type' => 'Script',
						'Execute on' => 'Zabbix proxy or server',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max script 3',
						'Scope' => 'Manual event action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'Script',
						'Execute on' => 'Zabbix proxy or server',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			// IPMI.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max IPMI',
						'Scope' => 'Manual host action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'IPMI',
						'Command' => 'IPMI command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Discovered hosts',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => 'Execute script?'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max IPMI 2',
						'Scope' => 'Action operation',
						'Type' => 'IPMI',
						'Command' => 'IPMI command',
						'Description' => 'Test description',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Discovered hosts'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max IPMI 3',
						'Scope' => 'Manual event action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'IPMI',
						'Command' => 'IPMI command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Discovered hosts',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => 'Execute script?'
					]
				]
			],
			// SSH.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max SSH',
						'Scope' => 'Manual host action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'SSH',
						'Username' => 'test',
						'Password' => 'test_password',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max SSH 2',
						'Scope' => 'Action operation',
						'Type' => 'SSH',
						'Username' => 'test',
						'Password' => 'test_password',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max SSH 3',
						'Scope' => 'Manual event action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'SSH',
						'Username' => 'test',
						'Password' => 'test_password',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max SSH 4',
						'Scope' => 'Manual event action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'SSH',
						'Authentication method' => 'Public key',
						'Username' => 'test',
						'Public key file' => 'public_key_file',
						'Private key file' => 'private_key_file',
						'Key passphrase' => 'key_passphrase',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max SSH 5',
						'Scope' => 'Action operation',
						'Type' => 'SSH',
						'Authentication method' => 'Public key',
						'Username' => 'test',
						'Public key file' => 'public_key_file',
						'Private key file' => 'private_key_file',
						'Key passphrase' => 'key_passphrase',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max SSH 6',
						'Scope' => 'Manual host action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'SSH',
						'Authentication method' => 'Public key',
						'Username' => 'test',
						'Public key file' => 'public_key_file',
						'Private key file' => 'private_key_file',
						'Key passphrase' => 'key_passphrase',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			// Telnet.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max Telnet',
						'Scope' => 'Manual host action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'Telnet',
						'Username' => 'test',
						'Password' => 'test_password',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max Telnet 2',
						'Scope' => 'Action operation',
						'Type' => 'Telnet',
						'Username' => 'test',
						'Password' => 'test_password',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Max Telnet 3',
						'Scope' => 'Manual event action',
						'Menu path' => 'path_1/path_2',
						'Type' => 'Telnet',
						'Username' => 'test',
						'Password' => 'test_password',
						'Port' => '81',
						'Commands' => 'Script command',
						'Description' => 'Test description',
						'User group' => 'Selenium user group',
						'Host group' => 'Selected',
						'xpath://div[@id="groupid"]/..' => 'Hypervisors',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => false
					]
				]
			],
			// URL.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'type URL for manual host action',
						'Scope' => 'Manual host action',
						'Menu path' => 'top_menu/sub_menu/',
						'Type' => 'URL',
						'URL' => 'http://zabbix.com',
						'Open in a new window' => false,
						'Description' => 'selected Url type',
						'Host group' => 'Selected',
						'User group' => 'Zabbix administrators',
						'xpath://div[@id="groupid"]/..' => 'Zabbix servers',
						'Required host permissions' => 'Write',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => 'open url?'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'type URL for manual event action',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => 'zabbix.php?action=script.list'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'User input type string',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => 'http://zabbix.com',
						'Open in a new window' => false,
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'Test version?',
						'Default input string' => 'Zabbix 7.0.0 alpha',
						'Input validation rule' => 'Zabbix [0-9]+\.[0-9]\.[0-9]+ alpha'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'User input type string - empty string',
						'Scope' => 'Manual event action',
						'Type' => 'Webhook',
						'Script' => 'ping localhost',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'empty string',
						'Default input string' => '',
						'Input validation rule' => '^$' // should match an empty string.
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'User input type - dropdown',
						'Scope' => 'Manual host action',
						'Type' => 'Script',
						'Execute on' => 'Zabbix server',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'dropdown values',
						'Input type' => 'Dropdown',
						'Dropdown options' => '.*,,A'
					]
				]
			],
			// User manual input cases with UTF-8 4-byte characters.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => '🔔User input type string with UTF-8 4-byte characters🔔',
						'Scope' => 'Manual event action',
						'Type' => 'SSH',
						'Username' => 'zabbix',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'string values 🚩',
						'Input type' => 'String',
						'Default input string' => '⚠️',
						'Input validation rule' => '⚠️'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => '❌User input type dropdown with UTF-8 4-byte characters❌',
						'Scope' => 'Manual host action',
						'Type' => 'Telnet',
						'Username' => 'zabbix',
						'Commands' => 'test',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => 'dropdown values 📌',
						'Input type' => 'Dropdown',
						'Dropdown options' => '📌,⚠️,❌'
					]
				]
			],
			// User manual input cases with maxlength.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => STRING_255,
						'Scope' => 'Manual host action',
						'Type' => 'IPMI',
						'Command' => 'ping localhost',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => STRING_255,
						'Input type' => 'String',
						'Default input string' => STRING_255,
						'Input validation rule' => str_repeat('.*|.*|.*', 256)
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => str_repeat('TEST_', 51),
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => STRING_2048,
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => STRING_255,
						'Input type' => 'Dropdown',
						'Dropdown options' => STRING_128.','.STRING_64.','.str_repeat('tests', 12)
					]
				]
			],
			// User manual input cases with macro.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Open Zabbix page',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => 'http://localhost/ui/zabbix.php?action={MANUALINPUT}',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Enable confirmation' => true,
						'Input prompt' => 'Zabbix page to open:',
						'Input type' => 'Dropdown',
						'Dropdown options' => 'dashboard.view,discovery.view',
						'Confirmation text' => 'Are you sure you want to open {MANUALINPUT} page?'
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Name' => 'Ping count',
						'Scope' => 'Manual host action',
						'Type' => 'Script',
						'Execute on' => 'Zabbix server',
						'Commands' => 'ping -c {MANUALINPUT} {HOST.CONN};',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Enable confirmation' => true,
						'Input prompt' => 'Add ping count',
						'Input type' => 'String',
						'Default input string' => '5',
						'Input validation rule' => '\b[1-9]\b',
						'Confirmation text' => 'Are you sure you want to execute ping script with value {MANUALINPUT}?'
					]
				]
			],
			// Check that manual input fields leading and trailing spaces and are trimmed.
			[
				[
					'expected' => TEST_GOOD,
					'trim' => true,
					'fields' => [
						'Name' => 'Trim check for dropdown input type',
						'Scope' => 'Manual host action',
						'Type' => 'SSH',
						'Username' => 'test',
						'Commands' => 'ping localhost;',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => '  Add spaces  ',
						'Input type' => 'Dropdown',
						'Dropdown options' => ' Q,A '
					]
				]
			],
			[
				[
					'expected' => TEST_GOOD,
					'trim' => true,
					'fields' => [
						'Name' => 'Trim check for string input type',
						'Scope' => 'Manual event action',
						'Type' => 'Script',
						'Execute on' => 'Zabbix server',
						'Commands' => 'ping localhost;',
						'Advanced configuration' => true,
						'Enable user input' => true,
						'Input prompt' => '  Add spaces  ',
						'Input type' => 'String',
						'Default input string' => ' 5 ',
						'Input validation rule' => ' \b[1-9]\b '
					]
				]
			]
		];
	}

	/**
	 * @dataProvider getScriptsData
	 * @backupOnce scripts
	 */
	public function testFormAlertsScripts_Create($data) {
		$this->checkScripts($data, false);
	}

	/**
	 * @dataProvider getScriptsData
	 */
	public function testFormAlertsScripts_Update($data) {
		$this->checkScripts($data, true, self::$ids['Script for Update']);
	}

	/**
	 * Function for checking script configuration form.
	 *
	 * @param array     $data     data provider
	 * @param boolean   $update   is it update case, or not
	 * @param int		$id       id of the script in case of updating
	 */
	private function checkScripts($data, $update, $id = null) {
		if ($data['expected'] === TEST_BAD) {
			$sql = 'SELECT * FROM scripts ORDER BY scriptid';
			$old_hash = CDBHelper::getHash($sql);
		}

		// Open the correct form - either edit existing script, or add new.
		$modal = $this->openScriptForm($id);
		$form = $modal->asForm();
		$form->fill($data['fields']);

		if (CTestArrayHelper::get($data, 'Parameters')) {
			// Remove action and index fields for create case.
			if ($update === false) {
				foreach ($data['Parameters'] as &$parameter) {
					unset($parameter['action'], $parameter['index']);
				}
				unset($parameter);
			}

			$modal->query('id:parameters-table')->asMultifieldTable()->one()->fill($data['Parameters']);
		}

		// Check testing confirmation while configuring.
		if (array_key_exists('Enable confirmation', $data['fields'])) {
			$this->checkConfirmation($data, $form);
		}

		$form->submit();
		$this->page->waitUntilReady();

		if ($data['expected'] === TEST_BAD) {
			$title = ($update) ? 'Cannot update script' : 'Cannot add script';
			$this->assertMessage(TEST_BAD, $title, $data['details']);

			// Check that DB hash is not changed.
			$this->assertEquals($old_hash, CDBHelper::getHash($sql));
		}
		else {
			$title = ($update) ? 'Script updated' : 'Script added';
			$this->assertMessage(TEST_GOOD, $title);
			$this->assertEquals(1, CDBHelper::getCount('SELECT NULL FROM scripts WHERE name='.zbx_dbstr($data['fields']['Name'])));

			// Check the results in form.
			$id = CDBHelper::getValue('SELECT scriptid FROM scripts WHERE name='.zbx_dbstr($data['fields']['Name']));
			$this->openScriptForm($id, false);

			if (array_key_exists('Advanced configuration', $data['fields'])) {
				$form->fill(['Advanced configuration' => true]);
			}

			// Trim trailing and leading spaces in expected values before comparison.
			if (CTestArrayHelper::get($data, 'trim', false)) {
				$data = CTestArrayHelper::trim($data);
			}

			$form->invalidate();
			$form->checkValue($data['fields']);

			// Check testing confirmation in saved form.
			if (array_key_exists('Enable confirmation', $data['fields'])) {
				$this->checkConfirmation($data, $form);
			}

			if (CTestArrayHelper::get($data, 'Parameters')) {
				// Remove action and index fields for asserting.
				if ($update === true) {
					foreach ($data['Parameters'] as &$parameter) {
						unset($parameter['action'], $parameter['index']);
					}
					unset($parameter);
				}

				$modal->query('id:parameters-table')->asMultifieldTable()->one()->checkValue($data['Parameters']);
			}
		}

		$modal->close();
	}

	/**
	 * Function for checking execution confirmation popup.
	 *
	 * @param array     $data    data provider
	 * @param element   $form    script configuration form
	 */
	private function checkConfirmation($data, $form) {
		if (CTestArrayHelper::get($data['fields'], 'Enable confirmation') === false) {
			$this->assertFalse($form->query('id:confirmation')->one()->isEnabled());
			$this->assertFalse($form->query('id:test_confirmation')->one()->isEnabled());
		}

		if (CTestArrayHelper::get($data['fields'], 'Confirmation text')) {
			$this->query('button:Test confirmation')->waitUntilClickable()->one()->click();
			$dialog = COverlayDialogElement::find()->waitUntilReady()->all()->last();
			$this->assertEquals($data['fields']['Confirmation text'],
					$dialog->query('xpath:.//span[@class="confirmation-msg"]')->waitUntilVisible()->one()->getText()
			);
			$dialog->query('class:btn-overlay-close')->waitUntilClickable()->one()->click();
		}
	}

	/**
	 * Function for checking script form update cancelling.
	 */
	public function testFormAlertsScripts_CancelUpdate() {
		$sql = 'SELECT * FROM scripts ORDER BY scriptid';
		$old_hash = CDBHelper::getHash($sql);
		$modal = $this->openScriptForm(self::$ids['Script for Update']);
		$modal->asForm()->fill([
			'Name' => 'Cancelled script',
			'Type' => 'Script',
			'Execute on' => 'Zabbix server',
			'Commands' => 'Script command',
			'Description' => 'Cancelled description',
			'User group' => 'Disabled',
			'Host group' => 'Selected',
			'xpath://div[@id="groupid"]/..' => 'Hypervisors',
			'Required host permissions' => 'Write',
			'Advanced configuration' => true,
			'Enable user input' => true,
			'Input prompt' => 'Insert value',
			'Default input string' => 'v1.1',
			'Input validation rule' => 'v[0-9]+\.[0-9]+',
			'Enable confirmation' => true,
			'Confirmation text' => 'Your configuration will be updated. Are you sure?'
		]);
		$modal->query('button:Cancel')->waitUntilClickable()->one()->click();
		$this->page->waitUntilReady();
		$this->page->assertHeader('Scripts');
		$this->assertTrue($this->query('button:Create script')->waitUntilVisible()->one()->isReady());
		$this->assertEquals($old_hash, CDBHelper::getHash($sql));
	}

	/**
	 * Function for checking script form update without any changes.
	 */
	public function testFormAlertsScripts_SimpleUpdate() {
		$sql = 'SELECT * FROM scripts ORDER BY scriptid';
		$old_hash = CDBHelper::getHash($sql);
		$modal = $this->openScriptForm(self::$ids['Script for Update']);
		$modal->asForm()->submit();
		$this->page->waitUntilReady();
		$this->assertMessage(TEST_GOOD, 'Script updated');
		$this->assertEquals($old_hash, CDBHelper::getHash($sql));
	}

	/**
	 * Function for checking script cloning with only changed name.
	 */
	public function testFormAlertsScripts_Clone() {
		$this->page->login();

		foreach (self::$clone_scriptids as $scriptid) {
			$modal = $this->openScriptForm($scriptid, false);
			$form = $modal->asForm();
			$values = $form->getFields()->asValues();
			$script_name = $values['Name'];
			$this->query('button:Clone')->waitUntilClickable()->one()->click();
			$this->page->waitUntilReady();

			$form->invalidate();
			$form->fill(['Name' => 'Cloned_'.$script_name]);
			$form->submit();

			$this->assertMessage(TEST_GOOD, 'Script added');
			$this->assertEquals(1, CDBHelper::getCount('SELECT NULL FROM scripts WHERE name='.zbx_dbstr($script_name)));
			$this->assertEquals(1, CDBHelper::getCount('SELECT NULL FROM scripts WHERE name='.zbx_dbstr('Cloned_'.$script_name)));

			$id = CDBHelper::getValue('SELECT scriptid FROM scripts WHERE name='.zbx_dbstr('Cloned_'.$script_name));
			$this->openScriptForm($id, false);
			$cloned_values = $form->getFields()->asValues();
			$this->assertEquals('Cloned_'.$script_name, $cloned_values['Name']);

			// Field Name removed from arrays.
			unset($cloned_values['Name']);
			unset($values['Name']);
			$this->assertEquals($values, $cloned_values);
			$modal->close();
		}
	}

	/**
	 * Function for testing script delete from configuration form.
	 */
	public function testFormAlertsScripts_Delete() {
		$modal = $this->openScriptForm(self::$ids['Script for Delete']);
		$modal->query('button:Delete')->waitUntilClickable()->one()->click();
		$this->page->acceptAlert();
		$this->page->waitUntilReady();
		$this->assertMessage(TEST_GOOD, 'Script deleted');
		$this->assertEquals(0, CDBHelper::getCount('SELECT NULL FROM scripts WHERE scriptid='.
				zbx_dbstr(self::$ids['Script for Delete']))
		);
	}

	/**
	 * Check all fields default values, lengths, placeholders, element options and table headers.
	 */
	public function testFormAlertsScripts_Layout() {
		$modal = $this->openScriptForm();
		$form = $modal->asForm();

		$default_values = ['Scope' => 'Action operation', 'Type' => 'Webhook', 'Host group' => 'All',
			'User group' => 'All', 'Required host permissions' => 'Read', 'Enable confirmation' => false, 'Timeout' => '30s',
			'Execute on' => 'Zabbix agent', 'Authentication method' => 'Password', 'Open in a new window' => true
		];
		$form->checkValue($default_values);

		// Check table headers.
		$this->assertEquals(['Name', 'Value', 'Action'], $form->query('id:parameters-table')->asTable()->one()->getHeadersText());

		// Check fields' lengths.
		$field_maxlength = [
			'Name' => 255,
			'Timeout' => 32,
			'Description' => 65535,
			'Menu path' => 255,
			'Confirmation text' => 255,
			'Commands' => 65535,
			'URL' => 2048,
			'Username' => 64,
			'Password' => 64,
			'Port' => 64,
			'Public key file' => 64,
			'Private key file' => 64,
			'Key passphrase' => 64,
			'Command' => 65535,
			'Input prompt' => 255,
			'Default input string' => 255,
			'Input validation rule' => 2048,
			'Dropdown options' => 2048
		];
		foreach ($field_maxlength as $input => $value) {
			$this->assertEquals($value, $form->getField($input)->getAttribute('maxlength'));
		}

		// Check fields' placeholders.
		$this->assertEquals('script', $form->getField('Script')->query('xpath:.//input[@type="text"]')->one()->getAttribute('placeholder'));
		$this->assertEquals('<sub-menu/sub-menu/...>', $form->getField('Menu path')->getAttribute('placeholder'));
		$this->assertEquals('regular expression', $form->getField('Input validation rule')->getAttribute('placeholder'));
		$this->assertEquals('comma-separated list', $form->getField('Dropdown options')->getAttribute('placeholder'));

		// Check dropdown options.
		$user_groups = CDBHelper::getColumn('SELECT name FROM usrgrp ORDER BY name', 'name');
		$dropdowns = [
			'Host group' => ['All', 'Selected'],
			'User group' => array_merge(['All'], $user_groups),
			'Authentication method' => ['Password', 'Public key']
		];
		foreach ($dropdowns as $field => $options) {
			$this->assertEquals($options, $form->getField($field)->getOptions()->asText());
		}

		// Check segmented radio element options.
		$segmented_elements = [
			'Scope' => ['Action operation', 'Manual host action', 'Manual event action'],
			'Type' => ['URL', 'Webhook', 'Script', 'SSH', 'Telnet', 'IPMI'],
			'Execute on' => ['Zabbix agent', 'Zabbix proxy or server', 'Zabbix server'],
			'Required host permissions' => ['Read', 'Write'],
			'Input type' => ['String', 'Dropdown']
		];
		foreach ($segmented_elements as $field => $options) {
			$this->assertEquals($options, $form->getField($field)->getLabels()->asText());
		}

		// Check "Script" dialog window.
		$script_dialog = $form->getField('Script')->edit();
		$this->assertEquals('JavaScript', $script_dialog->getTitle());
		$this->assertEquals(65535, $script_dialog->query('tag:textarea')->one()->getAttribute('maxlength'));
		$this->assertEquals('return value', $script_dialog->query('tag:textarea')->one()->getAttribute('placeholder'));
		$this->assertEquals('65535 characters remaining', $script_dialog->query('class:multilineinput-char-count')->one()->getText());
		$script_dialog->query('tag:textarea')->one()->type('aaa');
		$this->assertEquals('65532 characters remaining', $script_dialog->query('class:multilineinput-char-count')->one()->getText());
		$script_dialog->query('button:Cancel')->one()->click();
		$script_dialog->waitUntilNotVisible();
		$form->checkValue(['Script' => '']);

		// Check "Confirmation" and "Manual input" dialog windows.
		$scenarios = [
			'confirmation' => [
				'Scope' => 'Manual host action',
				'Advanced configuration' => true,
				'Enable confirmation' => true,
				'Confirmation text' => 'test'
			],
			'input_string' => [
				'Enable user input' => true,
				'Input prompt' => 'test',
				'Default input string' => 'v1.2',
				'Input validation rule' => 'v[0-9]+\.[0-9]+'
			],
			'input_dropdown' => [
				'Input prompt' => '{HOST.CONN}',
				'Input type' => 'Dropdown',
				'Dropdown options' => 'a,b,,c'
			]
		];
		foreach ($scenarios as $scenario => $parameters) {
			$form->fill($parameters);
			$this->query('button:'.(($scenario === 'confirmation') ? 'Test confirmation' : 'Test user input'))
					->waitUntilClickable()->one()->click();
			$dialog = COverlayDialogElement::find()->waitUntilReady()->all()->last();
			$this->assertEquals((($scenario === 'confirmation') ? 'Execution confirmation' : 'Manual input'), $dialog->getTitle());

			if ($scenario === 'confirmation') {
				$this->assertEquals($parameters['Confirmation text'], $dialog->query('class:confirmation-msg')
						->one()->getText()
				);
			}
			else {
				$this->assertEquals($parameters['Input prompt'], $dialog->query('class:wordbreak')->one()->getText());
			}

			if ($scenario === 'input_dropdown') {
				$this->assertEquals(['a', 'b', '', 'c'], $dialog->query('name:manualinput')->asDropdown()->one()
						->getOptions()->asText()
				);
			}

			if ($scenario === 'input_string') {
				$this->assertEquals($parameters['Default input string'], $dialog->query('id:manualinput')
						->one()->getValue()
				);
				$this->assertTrue($dialog->query('button:Test')->one()->isEnabled());
			}
			else {
				$this->assertFalse($dialog->query('button:'.(($scenario === 'confirmation') ? 'Execute' : 'Test'))
						->one()->isEnabled()
				);
			}

			$dialog->query('button:Cancel')->one()->click();
			$dialog->waitUntilNotVisible();
		}

		$modal->close();
	}

	/**
	 * Check the visible fields and their default values, and the required class based on the selected scope and type.
	 */
	public function testFormAlertsScripts_VisibleFields() {
		$common_all_scopes = [
			'fields' => ['Name', 'Scope', 'Type', 'Description', 'Host group'],
			'required' => ['Name'],
			'default' => ['Host group' => 'All']
		];
		$common_manual_scope = [
			'fields' => ['Menu path', 'User group', 'Required host permissions', 'Advanced configuration'],
			'default' => ['User group' => 'All', 'Required host permissions' => 'Read', 'Enable user input' => false,
				'Input prompt' => '', 'Input type' => 'String', 'Default input string' => '', 'Input validation rule' => '',
				'Enable confirmation' => false, 'Confirmation text' => ''
			],
			'advanced_fields' => ['Enable user input', 'Input prompt', 'Input type', 'Default input string', 'Input validation rule',
				'Enable confirmation', 'Confirmation text'
			],
			'advanced_fields_dropdown' => ['Enable user input', 'Input prompt', 'Input type', 'Dropdown options',
				'Enable confirmation', 'Confirmation text'
			],
			'input_string_required' => ['Input prompt', 'Input validation rule', 'Confirmation text'],
			'input_dropdown_required' => ['Input prompt', 'Dropdown options', 'Confirmation text']
		];
		$types = [
			'Webhook' => [
				'fields' => ['Parameters', 'Script', 'Timeout'],
				'required' => ['Script', 'Timeout'],
				'default' => ['Timeout' => '30s']
			],
			'Script' => [
				'fields' => ['Execute on', 'Commands'],
				'required' => ['Commands'],
				'default' => ['Execute on' => 'Zabbix agent']
			],
			'SSH' => [
				'fields' => ['Authentication method', 'Username', 'Password', 'Port', 'Commands'],
				'required' => ['Username', 'Commands'],
				'default' => ['Authentication method' => 'Password'],
				'fields_public_key' => ['Authentication method', 'Username', 'Public key file', 'Private key file',
					'Key passphrase', 'Port', 'Commands'
				],
				'required_public_key' => ['Username', 'Public key file', 'Private key file', 'Commands']
			],
			'Telnet' => [
				'fields' => ['Username', 'Password', 'Port', 'Commands'],
				'required' => ['Username', 'Commands'],
				'default' => []
			],
			'IPMI' => [
				'fields' => ['Command'],
				'required' => ['Command'],
				'default' => []
			],
			'URL' => [
				'fields' => ['URL', 'Open in a new window'],
				'required' => ['URL'],
				'default' => ['Open in a new window' => true]
			]
		];

		$modal = $this->openScriptForm();
		$form = $modal->asForm();
		$form->checkValue(['Scope' => 'Action operation', 'Type' => 'Webhook']);

		foreach (['Action operation', 'Manual host action', 'Manual event action'] as $scope) {
			// Merge all common fields based on scope type, manual or action operation.
			if ($scope === 'Action operation') {
				$scope_fields = $common_all_scopes['fields'];
				$scope_default = $common_all_scopes['default'];
				$scope_required = $common_all_scopes['required'];
			}
			else {
				$form->fill(['Scope' => $scope]);

				$scope_fields = array_merge($common_all_scopes['fields'], $common_manual_scope['fields'],
						$common_manual_scope['advanced_fields']
				);
				$scope_fields_dropdown = array_merge($common_all_scopes['fields'], $common_manual_scope['fields'],
						$common_manual_scope['advanced_fields_dropdown']
				);
				$scope_default = array_merge($common_all_scopes['default'], $common_manual_scope['default']);
				$scope_required = array_merge($common_all_scopes['required'], $common_manual_scope['input_string_required']);
				$scope_required_dropdown = array_merge($common_all_scopes['required'], $common_manual_scope['input_dropdown_required']);
			}

			foreach ($types as $type => $type_fields) {
				// Type 'URL' not visible for 'Action operation'.
				if ($scope === 'Action operation' && $type === 'URL') {
					continue;
				}

				$form->fill(['Type' => $type]);

				if ($scope === 'Manual host action' || $scope === 'Manual event action') {
					// Check advanced configuration default value.
					$form->checkValue(['Advanced configuration' => false]);

					// Check that the 'Advanced configuration' additional fields are hidden.
					foreach ($common_manual_scope['advanced_fields'] as $label) {
						$this->assertFalse($form->getLabel($label)->isDisplayed());
					}

					$form->fill(['Advanced configuration' => true]);
				}

				// Check visible fields.
				$this->assertEqualsCanonicalizing(array_merge($scope_fields, $type_fields['fields']),
						$form->getLabels(CElementFilter::VISIBLE)->asText()
				);

				// Check default values.
				$form->checkValue(array_merge($scope_default, $type_fields['default']));

				if ($scope === 'Manual host action' || $scope === 'Manual event action') {
					$form->fill(['Enable user input' => true, 'Enable confirmation' => true]);
				}

				$this->assertEqualsCanonicalizing(array_merge($scope_required, $type_fields['required']),
						$form->getRequiredLabels()
				);

				if ($type === 'SSH') {
					// Check fields with 'Public key' authentication method.
					$form->fill(['Authentication method' => 'Public key']);

					$this->assertEqualsCanonicalizing(array_merge($scope_fields, $type_fields['fields_public_key']),
							$form->getLabels(CElementFilter::VISIBLE)->asText()
					);
					$this->assertEqualsCanonicalizing(array_merge($scope_required, $type_fields['required_public_key']),
							$form->getRequiredLabels()
					);

					// Reset the value of the "Authentication method" field.
					$form->fill(['Authentication method' => 'Password']);
				}

				// Check required fields when 'Input type' is 'Dropdown'.
				if ($scope === 'Manual host action' || $scope === 'Manual event action') {
					$form->fill(['Input type' => 'Dropdown']);


					$this->assertEqualsCanonicalizing(array_merge($scope_fields_dropdown, $type_fields['fields']),
							$form->getLabels(CElementFilter::VISIBLE)->asText()
					);

					if ($type === 'SSH') {
						$form->fill(['Authentication method' => 'Public key']);
						$this->assertEqualsCanonicalizing(array_merge($scope_fields_dropdown, $type_fields['fields_public_key']),
								$form->getLabels(CElementFilter::VISIBLE)->asText()
						);
						$this->assertEqualsCanonicalizing(array_merge($scope_required_dropdown, $type_fields['required_public_key']),
								$form->getRequiredLabels()
						);

						// Reset the value of the "Authentication method" field.
						$form->fill(['Authentication method' => 'Password']);
					}
					else {
						$this->assertEqualsCanonicalizing(array_merge($scope_required_dropdown, $type_fields['required']),
								$form->getRequiredLabels()
						);
					}

					// Change advanced configuration to default state.
					$form->fill([
						'Input type' => 'String',
						'Enable user input' => false,
						'Enable confirmation' => false,
						'Advanced configuration' => false
					]);
				}
			}
		}

		$modal->close();
	}

	/**
	 * Modify the URI scheme validation rules and check the result for the URL type in script form.
	 */
	public function testFormAlertsScripts_UriScheme() {
		$invalid_schemes = ['dns://zabbix.com', 'message://zabbix.com'];
		$default_valid_schemes = ['http://zabbix.com', 'https://zabbix.com', 'ftp://zabbix.com', 'file://zabbix.com',
			'mailto://zabbix.com', 'tel://zabbix.com', 'ssh://zabbix.com'
		];

		$modal = $this->openScriptForm(self::$ids['URI schemes']);
		$form = $modal->asForm();

		// Check default URI scheme rules: http, https, ftp, file, mailto, tel, ssh.
		$this->assertUriScheme($form, $default_valid_schemes);
		$this->assertUriScheme($form, $invalid_schemes, TEST_BAD);

		// Change valid URI schemes on "Other configuration parameters" page.
		$modal->close();
		$this->page->open('zabbix.php?action=miscconfig.edit');
		$config_form = $this->query('name:otherForm')->asForm()->waitUntilVisible()->one();
		$config_form->fill(['id:validate_uri_schemes' => true, 'id:uri_valid_schemes' => 'dns,message']);
		$config_form->submit();
		$this->assertMessage(TEST_GOOD, 'Configuration updated');

		$this->openScriptForm(self::$ids['URI schemes'], false);
		$this->assertUriScheme($form, $default_valid_schemes, TEST_BAD);
		$this->assertUriScheme($form, $invalid_schemes);

		// Disable URI scheme validation.
		$modal->close();
		$this->page->open('zabbix.php?action=miscconfig.edit')->waitUntilReady();
		$config_form->fill(['id:validate_uri_schemes' => false]);
		$config_form->submit();
		$this->assertMessage(TEST_GOOD, 'Configuration updated');

		$this->openScriptForm(self::$ids['URI schemes'], false);
		$this->assertUriScheme($form, array_merge($default_valid_schemes, $invalid_schemes));
		$modal->close();
	}

	/**
	 * Fill in the URL field to check the uri scheme validation rules.
	 *
	 * @param CFormElement $form	form element of script
	 * @param array $data			url field data
	 * @param string $expected		expected result after script form submit, TEST_GOOD or TEST_BAD
	 */
	private function assertUriScheme($form, $data, $expected = TEST_GOOD) {
		foreach ($data as $scheme) {
			$form->fill(['URL' => $scheme]);
			$form->submit();

			if ($expected === TEST_GOOD) {
				$this->assertMessage(TEST_GOOD, 'Script updated');
				$this->openScriptForm(self::$ids['URI schemes'], false);
			}
			else {
				$this->assertMessage(TEST_BAD, 'Cannot update script', 'Invalid parameter "/1/url": unacceptable URL.');
				CMessageElement::find()->one()->close();
			}
		}
	}

	public function getContextMenuData() {
		return [
			// USER.* macros.
			[
				[
					'fields' => [
						'Name' => 'USER macros - manual host',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => '{USER.FULLNAME}, {USER.NAME}, {USER.SURNAME}, {USER.USERNAME}',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => '{USER.FULLNAME}, {USER.NAME}, {USER.SURNAME}, {USER.USERNAME}'
					],
					'resolved_macros' => 'Zabbix Administrator (Admin), Zabbix, Administrator, Admin',
					'host' => 'ЗАББИКС Сервер',
					'trigger' => 'Test trigger with tag'
				]
			],
			[
				[
					'fields' => [
						'Name' => 'USER macros - manual event',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => '{USER.FULLNAME}, {USER.NAME}, {USER.SURNAME}, {USER.USERNAME}',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => '{USER.FULLNAME}, {USER.NAME}, {USER.SURNAME}, {USER.USERNAME}'
					],
					'resolved_macros' => 'Zabbix Administrator (Admin), Zabbix, Administrator, Admin',
					'host' => 'ЗАББИКС Сервер',
					'trigger' => 'Test trigger with tag'
				]
			],
			// EVENT.* macros.
			[
				[
					'fields' => [
						'Name' => 'EVENT macros - manual host',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => '{EVENT.ID},{EVENT.NAME},{EVENT.NSEVERITY},{EVENT.SEVERITY},{EVENT.STATUS},{EVENT.VALUE}',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => '{EVENT.ID},{EVENT.NAME},{EVENT.NSEVERITY},{EVENT.SEVERITY},'.
								'{EVENT.STATUS},{EVENT.VALUE}'
					],
					'resolved_macros' => '{EVENT.ID},{EVENT.NAME},{EVENT.NSEVERITY},{EVENT.SEVERITY},{EVENT.STATUS},{EVENT.VALUE}',
					'host' => 'ЗАББИКС Сервер',
					'trigger' => 'Test trigger with tag'
				]
			],
			[
				[
					'fields' => [
						'Name' => 'EVENT macros - manual event',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => '{EVENT.ID},{EVENT.NAME},{EVENT.NSEVERITY},{EVENT.SEVERITY},{EVENT.STATUS},{EVENT.VALUE}',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => '{EVENT.ID},{EVENT.NAME},{EVENT.NSEVERITY},{EVENT.SEVERITY},'.
								'{EVENT.STATUS},{EVENT.VALUE}'
					],
					'resolved_macros' => '93,Test trigger with tag,2,Warning,PROBLEM,1',
					'host' => 'ЗАББИКС Сервер',
					'trigger' => 'Test trigger with tag'
				]
			],
			// HOST.* macros.
			[
				[
					'fields' => [
						'Name' => 'HOST macros - manual host',
						'Scope' => 'Manual host action',
						'Type' => 'URL',
						'URL' => '{HOST.ID},{HOST.CONN},{HOST.DNS},{HOST.HOST},{HOST.IP},{HOST.NAME}',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => '{HOST.ID},{HOST.CONN},{HOST.DNS},{HOST.HOST},{HOST.IP},{HOST.NAME}'
					],
					'resolved_macros' => '10084,127.0.0.1,,Test host,127.0.0.1,ЗАББИКС Сервер',
					'host' => 'ЗАББИКС Сервер',
					'trigger' => 'Test trigger with tag'
				]
			],
			[
				[
					'fields' => [
						'Name' => 'HOST macros - manual event',
						'Scope' => 'Manual event action',
						'Type' => 'URL',
						'URL' => '{HOST.ID},{HOST.CONN},{HOST.DNS},{HOST.HOST},{HOST.IP},{HOST.NAME}',
						'Advanced configuration' => true,
						'Enable confirmation' => true,
						'Confirmation text' => '{HOST.ID},{HOST.CONN},{HOST.DNS},{HOST.HOST},{HOST.IP},{HOST.NAME}'
					],
					'resolved_macros' => '10084,127.0.0.1,,Test host,127.0.0.1,ЗАББИКС Сервер',
					'host' => 'ЗАББИКС Сервер',
					'trigger' => 'Test trigger with tag'
				]
			]
		];
	}

	/**
	 * Check resolved macros in Host and Event context menu on Problems page.
	 *
	 * @dataProvider getContextMenuData
	 */
	public function testFormAlertsScripts_ContextMenu($data) {
		$modal = $this->openScriptForm();
		$form = $modal->asForm();

		$form->fill($data['fields']);
		$form->submit();
		$this->assertMessage(TEST_GOOD, 'Script added');

		$this->page->open('zabbix.php?action=problem.view');
		$table = $this->query('class:list-table')->asTable()->one();

		$with_script = ($data['fields']['Scope'] === 'Manual host action') ? $data['host'] : $data['trigger'];
		$without_script = ($data['fields']['Scope'] === 'Manual host action') ? $data['trigger'] : $data['host'];

		// Check resolved macros in confirmation popup.
		$table->query('link', $with_script)->one()->click();
		$popup = CPopupMenuElement::find()->waitUntilVisible()->one();
		$popup->fill($data['fields']['Name']);
		$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
		$this->assertEquals('URL opening confirmation', $dialog->getTitle());
		$this->assertEquals($data['resolved_macros'], $dialog->query('class:confirmation-msg')->one()->getText());

		// Check if buttons present and clickable.
		$this->assertEquals(['Cancel', 'Open URL'], $dialog->getFooter()->query('button')->all()
				->filter(CElementFilter::CLICKABLE)->asText()
		);
		$dialog->close();

		// Check that script link is not present in the context menu for other manual action.
		$table->query('link', $without_script)->one()->click();
		$this->assertEquals(0, CPopupMenuElement::find()->waitUntilVisible()->one()->getItems()
				->filter(CElementFilter::TEXT_PRESENT, $data['fields']['Name'])->count()
		);
	}

	/**
	 * Logs in, opens Script list and opens a Script form for editing or new.
	 *
	 * @param integer  $id          ID of the script to open
	 * @param boolean  $login       is a login needed or not
	 *
	 * @return COverlayDialogElement
	 */
	protected function openScriptForm($id = null, $login = true) {
		if ($login) {
			$this->page->login()->open('zabbix.php?action=script.list');
		}
		else {
			$this->page->open('zabbix.php?action=script.list');
		}

		if ($id) {
			$this->query('xpath://a[@data-scriptid='.CXPathHelper::escapeQuotes($id).']')->waitUntilClickable()->one()->click();
		}
		else {
			$this->query('button:Create script')->waitUntilClickable()->one()->click();
		}

		return COverlayDialogElement::find()->one()->waitUntilReady();
	}
}