<?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';

/**
 * @backup users
 *
 * @dataSource LoginUsers, UserPermissions
 *
 * @onBefore prepareData
 */
class testFormUser extends CWebTest {

	const SQL = 'SELECT * FROM users';
	const ZABBIX_LDAP_USER = 'John Zabbix';
	const UPDATE_USER = 'Tag-user';
	const UPDATE_PASSWORD = 'Zabbix_Test_123';

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

	public function prepareData() {
		// Create LDAP server.
		CDataHelper::call('userdirectory.create', [
			[
				'idp_type' => IDP_TYPE_LDAP,
				'name' => 'LDAP server #1',
				'host' => 'LDAP host',
				'port' => 389,
				'base_dn' => 'ou=Users,dc=example,dc=org',
				'bind_dn' => 'cn=ldap_search,dc=example,dc=org',
				'bind_password' => 'ldapsecretpassword',
				'search_attribute' => 'uid'
			]
		]);
		$userdirectoryids = CDataHelper::getIds('name');

		// Create group with frontend access -> LDAP and previously created LDAP server.
		CDataHelper::call('usergroup.create', [
			[
				'name' => 'Zabbix LDAP',
				'gui_access' => GROUP_GUI_ACCESS_LDAP,
				'userdirectoryid' => $userdirectoryids['LDAP server #1']
			]
		]);
		$usergrpids = CDataHelper::getIds('name');

		// Create user with frontend access -> LDAP.
		CDataHelper::call('user.create', [
			[
				'username' => self::ZABBIX_LDAP_USER,
				'passwd' => 'test5678',
				'roleid' => '3'
			],
			[
				'username' => 'LDAP change password button check',
				'passwd' => 'test5678',
				'roleid' => '2',
				'usrgrps' => [
					[
						'usrgrpid' => $usergrpids['Zabbix LDAP']
					]
				]
			]
		]);
	}

	public function getLayoutData() {
		return [
			[
				[
					'role' => '',
					'required' => ['Username', 'Password', 'Password (once again)', 'Refresh', 'Rows per page'],
					'default' => [
						'Username' => '',
						'Name' => '',
						'Last name' => '',
						'Groups' => '',
						'Password' => '',
						'Password (once again)' => '',
						'Language' => 'System default',
						'Time zone' => CDateTimeHelper::getTimeZoneFormat('System default'), // For local tests change system default 'Europe/Riga' to 'UTC'.
						'Theme' => 'System default',
						'Auto-login' => false,
						'id:autologout_visible' => false,
						'id:autologout' => '15m',
						'Refresh' => '30s',
						'Rows per page' => '50',
						'URL (after login)' => ''
					],
					'disabled' => ['id:autologout'],
					'enabled_buttons' => ['Add', 'Cancel', 'Select'],
					'hintbox_warning' => [
						'Language' => 'You are not able to choose some of the languages,'.
								' because locales for them are not installed on the web server.'
					]
				]
			],
			[
				[
					'user' => 'guest',
					'role' => 'Guest role',
					'required' => ['Username', 'Refresh', 'Rows per page'],
					'default' => [
						'Username' => 'guest',
						'Name' => '',
						'Last name' => '',
						'Groups' => ['Disabled', 'Guests', 'Internal'],
						'Refresh' => '30s',
						'Rows per page' => '50',
						'URL (after login)' => ''
					],
					// TODO: xpath should be replaced after ZBX-23936 fix.
					'disabled' => ['Username', 'button:Change password', 'xpath:.//button[@id="label-lang"]/..',
						'xpath:.//button[@id="label-timezone"]/..', 'xpath:.//button[@id="label-theme"]/..'
					],
					'disabled_values' => [
						'id:label-lang' => 'System default',
						'id:label-timezone' => CDateTimeHelper::getTimeZoneFormat('System default'),
						'id:label-theme' => 'System default'
					],
					'enabled_buttons' => ['Update', 'Delete', 'Cancel', 'Select'],
					'hintbox_warning' => [
						'Password' => 'Password can only be changed for users using the internal Zabbix authentication.'
					]
				]
			],
			[
				[
					'user' => 'Admin',
					'role' => 'Super admin role',
					'required' => ['Username', 'Current password', 'Password', 'Password (once again)', 'Refresh', 'Rows per page'],
					'default' => [
						'Username' => 'Admin',
						'Name' => 'Zabbix',
						'Last name' => 'Administrator',
						'Groups' => ['Internal', 'Zabbix administrators'],
						'Current password' => '',
						'Password' => '',
						'Password (once again)' => '',
						'Language' => 'System default',
						'Time zone' => CDateTimeHelper::getTimeZoneFormat('System default'),
						'Theme' => 'System default',
						'Auto-login' => true,
						'id:autologout_visible' => false,
						'id:autologout' => '15m',
						'Refresh' => '30s',
						'Rows per page' => '100',
						'URL (after login)' => ''
					],
					'disabled' => ['id:autologout', 'button:Delete'],
					'enabled_buttons' => ['Update', 'Cancel', 'Select'],
					'hintbox_warning' => [
						'Language' => 'You are not able to choose some of the languages,'.
								' because locales for them are not installed on the web server.'
					]
				]
			]
		];
	}

	/**
	 * @dataProvider getLayoutData
	 */
	public function testFormUser_Layout($data) {
		$this->page->login()->open('zabbix.php?action=user.list');
		$user = CTestArrayHelper::get($data, 'user', 'new');

		if ($user === 'new') {
			$this->query('button:Create user')->one()->click();
		}
		else {
			$this->query('link', $user)->waitUntilVisible()->one()->click();
		}

		$this->page->assertTitle('Configuration of users');
		$this->page->assertHeader('Users');

		// Check tabs available in the form.
		$form = $this->query('name:user_form')->asForm()->one();
		$this->assertEquals(['User', 'Media', 'Permissions'], $form->getTabs());

		// Check default values.
		if ($user === 'Admin') {
			foreach (['id:current_password', 'id:password1', 'id:password2'] as $field) {
				$this->assertFalse($form->query($field)->one(false)->isValid());
			}

			$form->query('button:Change password')->one()->click();
			$this->assertFalse($form->query('button:Change password')->one(false)->isValid());
		}

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

		foreach ($data['disabled'] as $locator) {
			$field = $form->getField($locator);
			$this->assertTrue($field->isDisplayed());
			$this->assertFalse($field->isEnabled());
		}

		if (array_key_exists('disabled_values', $data)) {
			foreach ($data['disabled_values'] as $element => $value) {
				$this->assertEquals($value, $form->query($element)->one()->getText());
			}
		}
		else {
			$inputs = [
				'Username' => [
					'maxlength' => '100'
				],
				'Name' => [
					'maxlength' => '100'
				],
				'Last name' => [
					'maxlength' => '100'
				],
				'id:user_groups__ms' => [
					'placeholder' => 'type here to search'
				],
				'Password' => [
					'maxlength' => '255'
				],
				'Password (once again)' => [
					'maxlength' => '255'
				],
				'Refresh' => [
					'maxlength' => '32'
				],
				'Rows per page' => [
					'maxlength' => '6'
				],
				'URL (after login)' => [
					'maxlength' => '2048'
				]
			];
			foreach ($inputs as $field => $attributes) {
				$this->assertTrue($form->getField($field)->isAttributePresent($attributes));
			}

			if ($user === 'Admin') {
				$this->assertTrue($form->getField('Current password')->isAttributePresent(['maxlength' => '255']));
			}

			$form->getLabel('Password')->query('xpath:.//button[@data-hintbox]')->one()->click();
			$hint = $this->query('xpath://div[@class="overlay-dialogue wordbreak"]')->waitUntilReady();
			$help_message = "Password requirements:\n".
					"must be at least 8 characters long\n".
					"must not contain user's name, surname or username\n".
					"must not be one of common or context-specific passwords";
			$this->assertEquals($help_message, $hint->one()->getText());
			$hint->query('class:btn-overlay-close')->one()->click();

			$info_message = 'Password is not mandatory for non internal authentication type.';
			$this->assertEquals($info_message, $form->query('xpath:.//div[contains(text(), '.
					CXPathHelper::escapeQuotes($info_message).')]')->one()->getText()
			);
		}

		// Check hintbox contains correct text message.
		foreach ($data['hintbox_warning'] as $field => $text) {
			$form->getField($field)->query('xpath:./..//button[@data-hintbox]')->one()->waitUntilClickable()->click();
			$hint = $this->query('xpath://div[@class="overlay-dialogue wordbreak"]')->asOverlayDialog()->waitUntilReady()->one();
			$this->assertEquals($text, $hint->getText());
			$hint->close();
		}

		// Check required fields.
		$this->assertEquals($data['required'], $form->getRequiredLabels());

		// Check that buttons are present and clickable.
		$this->assertEquals(count($data['enabled_buttons']), $form->query('button', $data['enabled_buttons'])->all()
				->filter(CElementFilter::CLICKABLE)->count()
		);

		// Check Media tab layout.
		$form->selectTab('Media');
		$this->assertEquals(['Media'], array_values($form->getLabels(CElementFilter::VISIBLE)->asText()));
		$media_tab = $form->query('id:mediaTab')->one();
		$media_table = $media_tab->asTable();

		$this->assertEquals(['Type', 'Send to', 'When active', 'Use if severity', 'Status', 'Action'],
				$media_table->getHeadersText()
		);

		$add_button = $media_tab->query('button:Add')->one();
		$this->assertTrue($add_button->isClickable());

		// Check that Media tab buttons are present.
		if ($user === 'Admin') {
			$buttons = ['Update', 'Cancel'];
		}
		elseif ($user === 'guest') {
			$buttons = ['Update', 'Delete', 'Cancel'];
		}
		else {
			$buttons = ['Add', 'Cancel'];
		}
		$this->assertEquals(count($buttons), $this->query('class:tfoot-buttons')->one()->query('button', $buttons)->all()
				->filter(CElementFilter::CLICKABLE)->count()
		);

		$add_button->click();
		$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
		$this->assertEquals('Media', $dialog->getTitle());
		$dialog_form = $dialog->asForm();

		$modal_form = [
			'fields' => ['Type', 'Send to', 'When active', 'Use if severity', 'Enabled'],
			'default' => [
				'Type' => 'Reference webhook',
				'Send to' => '',
				'When active' => '1-7,00:00-24:00',
				'id:severity_0' => true,
				'id:severity_1' => true,
				'id:severity_2' => true,
				'id:severity_3' => true,
				'id:severity_4' => true,
				'id:severity_5' => true,
				'Enabled' => true
			],
			'buttons' => ['Add', 'Cancel']
		];
		$this->assertEquals($modal_form['fields'], array_values($dialog_form->getLabels(CElementFilter::VISIBLE)->asText()));
		$dialog_form->checkValue($modal_form['default']);
		$this->assertEquals(2, $dialog->getFooter()->query('button', $modal_form['buttons'])->all()
				->filter(CElementFilter::CLICKABLE)->count()
		);
		$this->assertEquals(['Send to', 'When active'], $dialog_form->getRequiredLabels());
		$dialog->close();

		// Check Permissions tab layout.
		$form->selectTab('Permissions');
		$form->checkValue($data['role']);

		if ($user === 'Admin') {
			$this->assertFalse($form->getField('Role')->asMultiselect()->isEnabled());
			$this->assertFalse($form->isRequired('Role'));
		}
		else {
			$this->assertTrue($form->getField('id:roleid')->isEnabled());
			$this->assertTrue($form->isRequired('Role'));
		}

		if ($data['role'] === '') {
			$this->assertTrue($form->getField('id:roleid_ms')->isAttributePresent(['placeholder' => 'type here to search']));
		}
	}

	public function getCreateData() {
		return [
			// Username is already taken by another user.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Admin',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'User with username "Admin" already exists.'
				]
			],
			// Empty 'Username' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => '',
						'Groups' => 'Zabbix administrators',
						'Password' => 'zabbix',
						'Password (once again)' => 'zabbix'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Incorrect value for field "username": cannot be empty.'
				]
			],
			// Space as 'Username' field value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => '   ',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Incorrect value for field "username": cannot be empty.'
				]
			],
			// Empty 'Role' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test1',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'error_title' => 'Cannot add user',
					'error_details' => 'Field "roleid" is mandatory.'
				]
			],
			// Empty mandatory fields
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => ''
					],
					'error_title' => 'Cannot add user',
					'error_details' => [
						'Incorrect value for field "username": cannot be empty.',
						'Field "roleid" is mandatory.'
					]
				]
			],
			// 'Password' fields not specified.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test2',
						'Groups' => 'Zabbix administrators'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Incorrect value for field "Password": cannot be empty.'
				]
			],
			// Empty 'Password (once again)' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test3',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// Empty 'Password' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test4',
						'Groups' => 'Zabbix administrators',
						'Password (once again)' => 'test5678'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// 'Password' and 'Password (once again)' do not match.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test5',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'tEST5678'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// Empty 'Refresh' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test6',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => ''
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Incorrect value for field "refresh": cannot be empty.'
				]
			],
			// Digits in value of the 'Refresh' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test7',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => '123abc'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/refresh": a time unit is expected.'
				]
			],
			// Value of the 'Refresh' field too large.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test8',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => '3601'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/refresh": value must be one of 0-3600.'
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test_2h',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => '2h'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/refresh": value must be one of 0-3600.'
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test_61m',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => '61m'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/refresh": value must be one of 0-3600.'
				]
			],
			// Non-time unit value in 'Refresh' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test9',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => '00000000000001'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/refresh": a time unit is expected.'
				]
			],
			// 'Rows per page' field equal to '0'.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test10',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Rows per page' => '0'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/rows_per_page": value must be one of 1-999999.'
				]
			],
			// Non-numeric value of 'Rows per page' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test11',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Rows per page' => 'abc123'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/rows_per_page": value must be one of 1-999999.'
				]
			],
			// 'Autologout' below minimal value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test12',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '89'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test12_1m',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '1m'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// 'Autologout' above maximal value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test13',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '86401'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test13_1441m',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '1441m'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test13_25h',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '25h'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// 'Autologout' with a non-numeric value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test14',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => 'ninety'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/autologout": a time unit is expected.'
				]
			],
			// 'Autologout' with an empty value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test15',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => ''
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Incorrect value for field "autologout": cannot be empty.'
				]
			],
			// URL unacceptable.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test16',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'URL (after login)' => 'javascript:alert(123);'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/url": unacceptable URL.'
				]
			],
			// Incorrect URL protocol.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Negative_Test19',
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'URL (after login)' => 'snmp://zabbix.com'
					],
					'role' => 'Super admin role',
					'error_title' => 'Cannot add user',
					'error_details' => 'Invalid parameter "/1/url": unacceptable URL.'
				]
			],
			// Creating user by specifying only mandatory parameters.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'Mandatory_user',
						'Groups' => 'Guests',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'role' => 'Guest role'
				]
			],
			// Creating a user with optional parameters specified (including autologout) using Cyrillic charatcers.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'Оверлорд',
						'Name' => 'Антон Антонович',
						'Last name' => 'Антонов',
						'Groups' => ['Zabbix administrators'],
						'Password' => 'абвгдеЁж',
						'Password (once again)' => 'абвгдеЁж',
						'Theme' => 'High-contrast dark',
						'Auto-login' => false,
						'Refresh' => '0',
						'Rows per page' => '999999',
						'URL (after login)' => 'https://zabbix.com'
					],
					'role' => 'Admin role',
					'check_form' => true
				]
			],
			// Creating a user with punctuation symbols in password and optional parameters specified.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'Detailed user',
						'Name' => 'Bugs',
						'Last name' => 'Bunny',
						'Groups' => [
							'Selenium user group in configuration',
							'Zabbix administrators'
						],
						'Password' => '!@#$%^&*()_+',
						'Password (once again)' => '!@#$%^&*()_+',
						'Language' => 'English (en_US)',
						'Theme' => 'Dark',
						'Auto-login' => true,
						'Refresh' => '3600s',
						'Rows per page' => '1',
						'URL (after login)' => 'sysmaps.php'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '1d'
					],
					'role' => 'Admin role',
					'check_form' => true,
					'check_user' => true
				]
			],
			// Creating user without a user group.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'No_usergroup',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'role' => 'Super admin role'
				]
			],
			// Verification that field password is not mandatory for users with LDAP authentication.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'LDAP_user',
						'Groups' => 'LDAP user group'
					],
					'role' => 'Super admin role'
				]
			],
			// Verification that field password is not mandatory for users with no access to frontend.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'No_frontend_user',
						'Groups' => 'No access to the frontend'
					],
					'role' => 'User role'
				]
			]
		];
	}

	/**
	 * @dataProvider getCreateData
	 */
	public function testFormUser_Create($data) {
		$old_hash = CDBHelper::getHash(self::SQL);

		$this->page->login()->open('zabbix.php?action=user.edit');
		$form = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
		$form->fill($data['fields']);

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

		if (array_key_exists('role', $data)) {
			$form->selectTab('Permissions');
			$form->fill(['Role' => $data['role']]);
		}

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

		// Verify that the user was created.
		if ($data['expected'] === TEST_BAD) {
			$this->assertMessage(TEST_BAD, $data['error_title'], $data['error_details']);
			$this->assertEquals($old_hash, CDBHelper::getHash(self::SQL));
		}
		else {
			$this->assertMessage(TEST_GOOD, 'User added');
			$this->assertEquals(1, CDBHelper::getCount('SELECT userid FROM users WHERE username='.zbx_dbstr($data['fields']['Username'])));
		}

		if (CTestArrayHelper::get($data, 'check_form', false)) {
			$this->assertFormFields($data);
		}

		if (CTestArrayHelper::get($data, 'check_user', false)) {
			$this->assertUserParameters($data);
		}
	}

	/**
	 * Check the field values after creating or updating user.
	 */
	private function assertFormFields($data) {
		$userid = CDBHelper::getValue('SELECT userid FROM users WHERE username='.zbx_dbstr($data['fields']['Username']));
		$this->page->open('zabbix.php?action=user.edit&userid='.$userid);
		$form_update = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();

		// Verify that fields are updated.
		$check_fields = ['Username', 'Name', 'Last name', 'Language', 'Theme', 'Refresh', 'Rows per page', 'URL (after login)'];
		foreach ($check_fields as $field_name) {
			if (array_key_exists($field_name, $data['fields'])) {
				$this->assertEquals($data['fields'][$field_name], $form_update->getField($field_name)->getValue());
			}
		}

		$this->assertEquals($data['fields']['Groups'], $form_update->getField('Groups')->getSelected());

		if (CTestArrayHelper::get($data, 'auto_logout.checked', false)) {
			$this->assertTrue($form_update->getField('Auto-login')->isChecked(false));
		}
		else {
			$this->assertTrue($form_update->getField('Auto-login')->isChecked($data['fields']['Auto-login']));
		}

		if (array_key_exists('role', $data)) {
			$form_update->selectTab('Permissions');
			$this->assertEquals([$data['role']], $form_update->getField('Role')->getSelected());
		}
	}

	/**
	 * Login as user and check user profile parameters in UI.
	 */
	private function assertUserParameters($data) {
		try {
			$this->page->logout();
			// Log in with the created or updated user.
			$password = CTestArrayHelper::get($data['fields'], 'Password', $data['fields']['Password'] = self::UPDATE_PASSWORD);
			$this->page->userLogin($data['fields']['Username'], $password);
			// Verification of URL after login.
			$this->assertStringContainsString($data['fields']['URL (after login)'], $this->page->getCurrentURL());
			// Verification of the number of rows per page parameter.
			$rows = $this->query('name:frm_maps')->asTable()->waitUntilVisible()->one()->getRows();
			$this->assertEquals($data['fields']['Rows per page'], $rows->count());

			// Verification of default theme.
			$db_theme = CDBHelper::getValue('SELECT theme FROM users WHERE username='.zbx_dbstr($data['fields']['Username']));
			$color = $this->query('tag:body')->one()->getCSSValue('background-color');
			$stylesheet = $this->query('xpath://link[@rel="stylesheet"]')->one();
			$parts = explode('/', $stylesheet->getAttribute('href'));
			$file_time = explode('?', end($parts));
			$file = $file_time[0];

			if ($data['fields']['Theme'] === 'Dark') {
				$this->assertEquals('dark-theme', $db_theme);
				$this->assertEquals('dark-theme.css', $file);
				$this->assertEquals('rgba(14, 16, 18, 1)', $color);
			}
			else if ($data['fields']['Theme'] === 'High-contrast light') {
				$this->assertEquals('hc-light', $db_theme);
				$this->assertEquals('hc-light.css', $file);
				$this->assertEquals('rgba(255, 255, 255, 1)', $color);
			}

			$this->page->logout();
		}
		catch (Exception $e) {
			$this->page->logout();
			throw $e;
		}
	}

	public function getUpdateData() {
		return [
			// #0 Incorrect current password.
			[
				[
					'expected' => TEST_BAD,
					'user_to_update' => 'Admin',
					'fields' => [
						'Current password' => 'azazabbix',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect current password.'
				]
			],
			// #1 Current password with spaces.
			[
				[
					'expected' => TEST_BAD,
					'user_to_update' => 'Admin',
					'fields' => [
						'Current password' => ' zabbix ',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect current password.'
				]
			],
			// #2 Empty current password.
			[
				[
					'expected' => TEST_BAD,
					'user_to_update' => 'Admin',
					'fields' => [
						'Password' => 'test5678',
						'Password (once again)' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect value for field "Current password": cannot be empty'
				]
			],
			// #3 Empty password fields for user without user group.
			[
				[
					'expected' => TEST_BAD,
					'user_to_update' => self::ZABBIX_LDAP_USER,
					'fields' => [
						'Password' => '',
						'Password (once again)' => ''
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect value for field "Password": cannot be empty'
				]
			],
			// #4 Username is already taken by another user.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => 'Admin'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'User with username "Admin" already exists.'
				]
			],
			// #5 Empty 'Username' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Username' => ''
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect value for field "username": cannot be empty.'
				]
			],
			// #6 Empty 'Password (once again)' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Password' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// #7 Empty 'Password' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Password (once again)' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// #8 LDAP user with empty repeated password.
			[
				[
					'expected' => TEST_BAD,
					'user_to_update' => 'LDAP user',
					'fields' => [
						'Password' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// #9 Updating user from "No access to the frontend" group without filling in password.
			[
				[
					'expected' => TEST_BAD,
					'user_to_update' => 'no-access-to-the-frontend',
					'fields' => [
						'Password (once again)' => 'test5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// #10 'Password' and 'Password (once again)' do not match.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'teST5678'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Both passwords must be equal.'
				]
			],
			// #11 Empty 'Refresh' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Groups' => 'Zabbix administrators',
						'Password' => 'test5678',
						'Password (once again)' => 'test5678',
						'Refresh' => ''
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect value for field "refresh": cannot be empty.'
				]
			],
			// #12 Digits in value of the 'Refresh' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Refresh' => '123abc'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/refresh": a time unit is expected.'
				]
			],
			// #13 Value of the 'Refresh' field too large.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Refresh' => '3601'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/refresh": value must be one of 0-3600.'
				]
			],
			// #14.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Refresh' => '61m'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/refresh": value must be one of 0-3600.'
				]
			],
			// #15 Non time unit value in 'Refresh' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Refresh' => '00000000000001'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/refresh": a time unit is expected.'
				]
			],
			// #16 'Rows per page' field equal to '0'.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Rows per page' => '0'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/rows_per_page": value must be one of 1-999999.'
				]
			],
			// #17 Non-numeric value of 'Rows per page' field.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'Rows per page' => 'abc123'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/rows_per_page": value must be one of 1-999999.'
				]
			],
			// #18 'Autologout' below minimal value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => '89'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// #19 'Autologout' above maximal value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => '86401'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// #20.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => '1m'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// #21.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => '1441m'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// #22.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => '25h'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/autologout": value must be one of 0, 90-86400.'
				]
			],
			// #23 'Autologout' with a non-numeric value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => 'ninety'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/autologout": a time unit is expected.'
				]
			],
			// #24 'Autologout' with an empty value.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [],
					'auto_logout' => [
						'checked' => true,
						'value' => ''
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Incorrect value for field "autologout": cannot be empty.'
				]
			],
			// #25 URL unacceptable.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'URL (after login)' => 'javascript:alert(123);'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/url": unacceptable URL.'
				]
			],
			// #26 Incorrect URL protocol.
			[
				[
					'expected' => TEST_BAD,
					'fields' => [
						'URL (after login)' => 'snmp://zabbix.com'
					],
					'error_title' => 'Cannot update user',
					'error_details' => 'Invalid parameter "/1/url": unacceptable URL.'
				]
			],
			// #27 Updating LDAP user with empty password fields.
			[
				[
					'expected' => TEST_GOOD,
					'user_to_update' => 'LDAP user',
					'fields' => [
						'Username' => 'LDAP user updated',
						'Password' => '',
						'Password (once again)' => ''
					]
				]
			],
			// #28 Updating user from "No access to the frontend" group using empty password fields.
			[
				[
					'expected' => TEST_GOOD,
					'user_to_update' => 'no-access-to-the-frontend',
					'fields' => [
						'Username' => 'no-access-to-the-frontend-updated',
						'Password' => '',
						'Password (once again)' => ''
					]
				]
			],
			// #29 Updating user from "LDAP" group.
			[
				[
					'expected' => TEST_GOOD,
					'user_to_update' => 'LDAP change password button check',
					'fields' => [
						'Username' => 'LDAP change password button check - updated'
					]
				]
			],
			// #30 Updating all fields (except password) of an existing user.
			[
				[
					'expected' => TEST_GOOD,
					'user_to_update' => 'disabled-user',
					'fields' => [
						'Username' => 'Updated_user_1',
						'Name' => 'Test_Name',
						'Last name' => 'Test_Surname',
						'Groups' => [
							'Selenium user group in configuration'
						],
						'Language' => 'English (en_US)',
						'Theme' => 'Dark',
						'Auto-login' => true,
						'Refresh' => '60m',
						'Rows per page' => '1',
						'URL (after login)' => 'sysmaps.php'
					],
					'auto_logout' => [
						'checked' => true,
						'value' => '24h'
					],
					'check_form' => true
				]
			],
			// #31.
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => 'Updated_user',
						'Name' => 'Road',
						'Last name' => 'Runner',
						'Groups' => [],
						'Language' => 'English (en_US)',
						'Theme' => 'High-contrast light',
						'Auto-login' => true,
						'Refresh' => '1h',
						'Rows per page' => '1',
						'URL (after login)' => 'sysmaps.php'
					],
					'check_form' => true,
					'check_user' => true
				]
			]
		];
	}

	/**
	 * @dataProvider getUpdateData
	 */
	public function testFormUser_Update($data) {
		$update_user = CTestArrayHelper::get($data, 'user_to_update', self::UPDATE_USER);

		if ($data['expected'] === TEST_BAD) {
			$old_hash = CDBHelper::getHash(self::SQL);
		}

		$this->page->login()->open('zabbix.php?action=user.list');
		$this->query('link', $update_user)->waitUntilVisible()->one()->click();

		// Update user parameters.
		$form = $this->query('name:user_form')->asForm()->one();

		if (array_key_exists('Password', $data['fields']) || array_key_exists('Password (once again)', $data['fields'])) {
			$form->query('button:Change password')->one()->click();
		}

		if ($update_user === 'LDAP change password button check') {
			$this->assertFalse($form->query('button:Change password')->one()->isClickable());
			$hintbox = 'Password can only be changed for users using the internal Zabbix authentication.';
			$this->assertEquals($hintbox, $this->query('xpath://button[contains(@data-hintbox-contents, '.
					CXPathHelper::escapeQuotes($hintbox).')]')->one()->getAttribute('data-hintbox-contents')
			);
		}

		if ($update_user === 'Admin') {
			$this->assertTrue($form->query('id:current_password')->one()->isVisible());
		}
		else {
			$this->assertFalse($form->query('id:current_password')->one(false)->isValid());
		}

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

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

		$form->submit();

		if (array_key_exists('Password', $data['fields']) && array_key_exists('Password (once again)', $data['fields'])) {
			if ($update_user === 'LDAP user' || $update_user === 'no-access-to-the-frontend' || $update_user === self::ZABBIX_LDAP_USER) {
				$this->assertFalse($this->page->isAlertPresent());
			}
			else {
				$this->assertTrue($this->page->isAlertPresent());
				$this->assertEquals('In case of successful password change user will be logged out of all active sessions. Continue?',
					$this->page->getAlertText()
				);
				$this->page->acceptAlert();
			}
		}

		$this->page->waitUntilReady();

		// Verify if the user was updated.
		if ($data['expected'] === TEST_BAD) {
			$this->assertMessage(TEST_BAD, $data['error_title'], $data['error_details']);
			$this->assertEquals($old_hash, CDBHelper::getHash(self::SQL));
		}
		else {
			$this->assertMessage(TEST_GOOD, 'User updated');
			$this->assertEquals(1, CDBHelper::getCount('SELECT userid FROM users WHERE username='.zbx_dbstr($data['fields']['Username'])));
		}

		if (CTestArrayHelper::get($data, 'check_form', false)) {
			$this->assertFormFields($data);
		}

		if (CTestArrayHelper::get($data, 'check_user', false)) {
			$this->assertUserParameters($data);
		}
	}

	/**
	 * Test update without any modification of user data.
	 */
	public function testFormUser_SimpleUpdate() {
		$sql_hash = 'SELECT * FROM users ORDER BY userid';
		$old_hash = CDBHelper::getHash($sql_hash);

		$this->page->login()->open('zabbix.php?action=user.list');
		$this->query('link', 'test-user')->waitUntilVisible()->one()->click();

		$form = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
		$form->submit();
		$this->page->waitUntilReady();
		$message = CMessageElement::find()->one();
		$this->assertTrue($message->isGood());
		$this->assertEquals('User updated', $message->getTitle());

		$this->assertEquals($old_hash, CDBHelper::getHash($sql_hash));
	}

	public function getPasswordUpdateData() {
		return [
			[
				[
					'username' => 'user-zabbix',
					'old_password' => 'test5678',
					'new_password' => 'test5678_new',
					'error_message' => 'Incorrect user name or password or account is temporarily blocked.',
					'attempt_message' => '1 failed login attempt logged. Last failed attempt was from'
				]
			],
			[
				[
					'username' => 'Admin',
					'old_password' => 'zabbix',
					'new_password' => 'test6789_new',
					'error_message' => 'Incorrect user name or password or account is temporarily blocked.',
					'attempt_message' => '1 failed login attempt logged. Last failed attempt was from'
				]
			]
		];
	}

	/**
	 * @dataProvider getPasswordUpdateData
	 *
	 * Test user password change and sign in with new password.
	 */
	public function testFormUser_PasswordUpdate($data) {
		$update_user = CTestArrayHelper::get($data, 'username', 'Admin');
		$this->page->login()->open('zabbix.php?action=user.list');
		$this->query('link', $update_user)->waitUntilVisible()->one()->click();
		$form_update = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
		$form_update->query('button:Change password')->one()->click();

		// Change user password and log out.
		if ($update_user === 'Admin') {
			$form_update->fill(['Current password' => $data['old_password']]);
		}

		$form_update->fill([
			'Password' => $data['new_password'],
			'Password (once again)' => $data['new_password']
		]);
		$form_update->submit();

		$this->assertTrue($this->page->isAlertPresent());
		$this->assertEquals('In case of successful password change user will be logged out of all active sessions. Continue?',
				$this->page->getAlertText()
		);
		$this->page->acceptAlert();

		try {
			$this->page->logout();

			// Attempt to sign in with old password.
			$this->page->userLogin($data['username'], $data['old_password']);
			$message = $this->query('class:red')->one()->getText();
			$this->assertEquals($message, $data['error_message']);

			// Sign in with new password.
			$this->page->userLogin($data['username'], $data['new_password']);
			$attempt_message = CMessageElement::find()->one();
			$this->assertTrue($attempt_message->hasLine($data['attempt_message']));
			$this->page->logout();
		}
		catch (\Exception $e) {
			// Logout to execute remaining tests.
			$this->page->logout();
			throw $e;
		}
	}

	public function getDeleteData() {
		return [
			[
				[
					'expected' => TEST_GOOD,
					'fields' => [
						'Username' => self::ZABBIX_LDAP_USER
					]
				]
			],
			// Attempt to delete internal user guest.
			[
				[
					'expected' => TEST_BAD,
					'username' => 'guest',
					'error_details' => 'Cannot delete Zabbix internal user "guest", try disabling that user.'
				]
			],
			// Attempt to delete a user that owns a map.
			[
				[
					'expected' => TEST_BAD,
					'username' => 'user-zabbix',
					'parameters' => [
						'DB_table' => 'sysmaps',
						'column' => 'name',
						'value' => 'Local network'
					],
					'error_details' => 'User "user-zabbix" is map "Local network" owner.'
				]
			],
			// Attempt to delete a user that owns a dashboard.
			[
				[
					'expected' => TEST_BAD,
					'username' => 'test-timezone',
					'error_details' => 'User "test-timezone" is dashboard "Testing share dashboard" owner.'
				]
			],
			// Attempt to delete a user that is mentioned in an action.
			[
				[
					'expected' => TEST_BAD,
					'username' => 'user-for-blocking',
					'error_details' => 'User "user-for-blocking" is used in "Action with user" action.'
				]
			]
		];
	}

	/**
	 * @dataProvider getDeleteData
	 */
	public function testFormUser_Delete($data) {
		// Defined required variables.
		if (array_key_exists('username', $data)) {
			$username = $data['username'];
		}
		else {
			$username = $data['fields']['Username'];
		}

		$this->page->login()->open('zabbix.php?action=user.list');
		$this->query('link', $username)->one()->click();
		$userid = CDBHelper::getValue('SELECT userid FROM users WHERE username='.zbx_dbstr($username));

		// Link user with map, action to validate user deletion.
		if (array_key_exists('parameters', $data)) {
			DBexecute(
					'UPDATE '.$data['parameters']['DB_table'].' SET userid ='.zbx_dbstr($userid).
					' WHERE '.$data['parameters']['column'].'='.zbx_dbstr($data['parameters']['value'])
			);
		}

		// Attempt to delete the user from user update view and verify result.
		$this->query('button:Delete')->one()->click();
		$this->page->acceptAlert();
		$this->page->waitUntilReady();

		// Validate if the user was deleted.
		if ($data['expected'] === TEST_BAD) {
			$this->assertMessage(TEST_BAD, 'Cannot delete user', $data['error_details']);
			$this->assertEquals(1, CDBHelper::getCount('SELECT userid FROM users WHERE username='.zbx_dbstr($username)));
		}
		else {
			$this->assertMessage(TEST_GOOD, 'User deleted');
			$this->assertEquals(0, CDBHelper::getCount('SELECT userid FROM users WHERE username='.zbx_dbstr($data['fields']['Username'])));
		}
	}

	/**
	 * Check that user can't delete oneself.
	 */
	public function testFormUser_SelfDeletion() {
		$this->page->login()->open('zabbix.php?action=user.edit&userid=1');
		$this->assertTrue($this->query('button:Delete')->waitUntilVisible()->one()->isEnabled(false));
	}

	public function testFormUser_Cancel() {
		$data = [
			'Username' => 'user-cancel',
			'Password' => 'zabbix',
			'Password (once again)' => 'zabbix',
			'Groups' => 'Guests'
		];
		$sql_users = 'SELECT * FROM users ORDER BY userid';
		$user_hash = CDBHelper::getHash($sql_users);
		$this->page->login()->open('zabbix.php?action=user.edit');

		// Check cancellation when creating users.
		$form_create = $this->query('name:user_form')->asForm()->waitUntilVisible()->one();
		$form_create->fill($data);
		$this->query('button:Cancel')->one()->click();
		$cancel_url = $this->page->getCurrentURL();
		$this->assertStringContainsString('zabbix.php?action=user.list', $cancel_url);
		$this->assertEquals($user_hash, CDBHelper::getHash($sql_users));

		// Check Cancellation when updating users.
		$this->page->open('zabbix.php?action=user.edit&userid=1');
		$this->query('id:name')->one()->fill('Boris');
		$this->query('button:Cancel')->one()->click();
		$this->assertEquals($user_hash, CDBHelper::getHash($sql_users));
	}

	private function setAutoLogout($data) {
		$form = $this->query('name:user_form')->asForm()->one();
		$auto_logout = $form->getFieldContainer('Auto-logout');
		$auto_logout->query('id:autologout_visible')->asCheckbox()->one()->set($data['checked']);
		if (array_key_exists('value', $data)) {
			$auto_logout->query('id:autologout')->one()->overwrite($data['value']);
		}
		// Verify that Auto-login is unchecked after setting Auto-logout.
		$this->assertTrue($form->getField('Auto-login')->isChecked(false));
	}
}