You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
zabbix/ui/tests/selenium/testPageAdministrationGener...

1407 lines
45 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
require_once dirname(__FILE__).'/../include/CWebTest.php';
require_once dirname(__FILE__).'/traits/TableTrait.php';
require_once dirname(__FILE__).'/behaviors/CMessageBehavior.php';
/**
* @backup module, widget
*/
class testPageAdministrationGeneralModules extends CWebTest {
use TableTrait;
/**
* Attach MessageBehavior to the test.
*
* @return array
*/
public function getBehaviors() {
return [
'class' => CMessageBehavior::class
];
}
private static $dashboardid;
private static $template_dashboardid;
private static $hostid;
const TEMPLATEID = 50000;
const ITEMID = 400410;
const INACCESSIBLE_TEXT = 'No permissions to referred object or it does not exist!';
const INACCESSIBLE_XPATH = 'xpath:.//div[contains(@class, "dashboard-widget-inaccessible")]';
const HOSTNAME = 'Host for widget module test';
private static $widget_descriptions = [
'Action log' => 'Displays records about executed action operations (notifications, remote commands).',
'Clock' => 'Displays local, server, or specified host time.',
'Data overview' => 'Displays the latest item data and current status of each item for selected hosts.',
'Discovery status' => 'Displays the status summary of the active network discovery rules.',
'Favorite graphs' => 'Displays shortcuts to the most needed graphs (marked as favorite).',
'Favorite maps' => 'Displays shortcuts to the most needed network maps (marked as favorite).',
'Gauge' => 'Displays the value of a single item as gauge.',
'Geomap' => 'Displays hosts as markers on a geographical map.',
'Graph' => 'Displays data of up to 50 items as line, points, staircase, or bar charts.',
'Graph (classic)' => 'Displays a single custom graph or a simple graph.',
'Graph prototype' => 'Displays a grid of graphs created by low-level discovery from either a graph prototype or '.
'an item prototype.',
'Host availability' => 'Displays the host count by status (available/unavailable/unknown).',
'Item value' => 'Displays the value of a single item prominently.',
'Map' => 'Displays either a single configured network map or one of the configured network maps in the map '.
'navigation tree.',
'Map navigation tree' => 'Allows to build a hierarchy of existing maps and display problem statistics for each '.
'included map and map group.',
'Plain text' => 'Displays the latest data for the selected items in plain text.',
'Problem hosts' => 'Displays the problem count by host group and the highest problem severity within a group.',
'Problems' => 'Displays currently open problems with quick access links to the problem details.',
'Problems by severity' => 'Displays the problem count by severity.',
'SLA report' => 'Displays SLA reports.',
'System information' => 'Displays the current status and system statistics of the Zabbix server and its '.
'associated components.',
'Top hosts' => 'Displays top N hosts that have the highest or the lowest item value (for example, CPU load) '.
'with an option to add progress-bar visualizations and customize report columns.',
'Top triggers' => 'Displays top N triggers that have the most problems within the period of evaluation,'.
' sorted by the number of problems.',
'Trigger overview' => 'Displays trigger states for selected hosts.',
'URL' => 'Displays the content retrieved from the specified URL.',
'Web monitoring' => 'Displays the status summary of the active web monitoring scenarios.'
];
/**
* Creates dashboards with widgets and defines the corresponding dashboard IDs.
*/
public static function prepareDashboardData() {
$response = CDataHelper::call('dashboard.create', [
[
'name' => 'Dashboard for widget module testing',
'private' => 0,
'auto_start' => 0,
'pages' => [
[
'name' => 'Map page',
'widgets' => [
[
'type' => 'navtree',
// TODO: Uncomment the below line when ZBX-22245 will be resolved.
// 'name' => 'Awesome map tree',
'x' => 0,
'y' => 0,
'width' => 12,
'height' => 4,
'view_mode' => 0,
'fields' => [
[
'type' => 1,
'name' => 'reference',
'value' => 'GZCSV'
],
[
'type' => 1,
'name' => 'navtree.name.1',
'value' => 'Awesome map'
],
[
'type' => 8,
'name' => 'navtree.sysmapid.1',
'value' => 1
]
]
],
[
'type' => 'map',
'x' => 12,
'y' => 0,
'width' => 12,
'height' => 4,
'view_mode' => 0,
'fields' => [
[
'type' => 0,
'name' => 'source_type',
'value' => 2
],
[
'type' => 1,
'name' => 'filter_widget_reference',
'value' => 'GZCSV'
]
]
],
[
'type' => 'favgraphs',
'view_mode' => 0,
'x' => 6,
'y' => 4,
'width' => 6,
'height' => 4
]
]
],
[
'name' => 'Alarm clock page',
'widgets' => [
[
'type' => 'clock345',
'view_mode' => 0,
'x' => 0,
'y' => 0,
'width' => 6,
'height' => 4
],
[
'type' => 'favgraphs',
'view_mode' => 0,
'x' => 6,
'y' => 0,
'width' => 6,
'height' => 4
]
]
],
[
'name' => 'System info page',
'widgets' => [
[
'type' => 'favgraphs',
'view_mode' => 0,
'x' => 0,
'y' => 0,
'width' => 6,
'height' => 4
],
[
'type' => 'systeminfo',
'view_mode' => 0,
'x' => 6,
'y' => 0,
'width' => 6,
'height' => 4
]
]
],
[
'name' => 'Empty widget page',
'widgets' => [
[
'type' => 'favgraphs',
'view_mode' => 0,
'x' => 0,
'y' => 0,
'width' => 6,
'height' => 4
],
[
'type' => 'emptyWidget',
'view_mode' => 0,
'x' => 6,
'y' => 0,
'width' => 6,
'height' => 4
]
]
]
]
]
]);
self::$dashboardid = $response['dashboardids'][0];
$template_responce = CDataHelper::call('templatedashboard.create', [
[
'templateid' => self::TEMPLATEID,
'name' => 'Templated dashboard for module widgets',
'auto_start' => 0,
'pages' => [
[
'name' => 'Default clock page',
'widgets' => [
[
'type' => 'clock',
// TODO: Uncomment the below line when ZBX-22245 will be resolved.
// 'name' => 'Default clock',
'width' => 6,
'height' => 4
],
[
'type' => 'item',
'x' => 6,
'y' => 0,
'width' => 6,
'height' => 4,
'fields' => [
[
'type' => 0,
'name' => 'itemid',
'value' => self::ITEMID
]
]
]
]
],
[
'name' => 'Alarm clock page',
'widgets' => [
[
'type' => 'clock',
'name' => 'Clock widget',
'width' => 6,
'height' => 4
],
[
'type' => 'clock345',
'view_mode' => 0,
'x' => 6,
'y' => 0,
'width' => 6,
'height' => 4,
'fields' => [
[
'type' => 0,
'name' => 'time_type',
'value' => 1
],
[
'type' => 1,
'name' => 'tzone_timezone',
'value' => 'local'
]
]
]
]
]
]
]
]);
self::$template_dashboardid = $template_responce['dashboardids'][0];
$host_responce = CDataHelper::createHosts([
[
'host' => self::HOSTNAME,
'interfaces' => [
'type' => INTERFACE_TYPE_AGENT,
'main' => 1,
'useip' => 1,
'ip' => '127.0.0.1',
'dns' => '',
'port' => '10050'
],
'groups' => [
'groupid' => 7
],
'status' => HOST_STATUS_MONITORED,
'templates' => [
'templateid' => self::TEMPLATEID
]
]
]);
self::$hostid = $host_responce['hostids'][self::HOSTNAME];
}
public function testPageAdministrationGeneralModules_Layout() {
$modules = [
[
'Name' => '1st Module name',
'Version' => '1',
'Author' => '1st Module author',
'Description' => '1st Module description',
'Status' => 'Disabled'
],
[
'Name' => '2nd Module name !@#$%^&*()_+',
'Version' => 'two !@#$%^&*()_+',
'Author' => '2nd Module author !@#$%^&*()_+',
'Description' => 'Module description !@#$%^&*()_+',
'Status' => 'Disabled'
],
[
'Name' => '4th Module',
'Version' => '',
'Author' => '',
'Description' => '',
'Status' => 'Disabled'
],
[
'Name' => '5th Module',
'Version' => '',
'Author' => '',
'Description' => 'Adding top-level and sub-level menu',
'Status' => 'Disabled'
],
[
'Name' => 'Clock2',
'Version' => '1.1',
'Author' => 'Zabbix QA department',
'Description' => '',
'Status' => 'Disabled'
],
[
'Name' => 'Empty widget',
'Version' => '1.0',
'Author' => 'Some Zabbix employee',
'Description' => '',
'Status' => 'Disabled'
],
[
'Name' => 'шестой модуль',
'Version' => 'бета 2',
'Author' => 'Работник Заббикса',
'Description' => 'Удалить "Reports" из меню верхнего уровня, а так же удалить "Maps" из секции "Monitoring".',
'Status' => 'Disabled'
]
];
// Create an array with widget modules that should be present by default.
$widget_modules = [];
$i = 0;
foreach (self::$widget_descriptions as $name => $description) {
$widget_modules[$i]['Name'] = $name;
$widget_modules[$i]['Version'] = '1.0';
$widget_modules[$i]['Author'] = 'Zabbix';
$widget_modules[$i]['Description'] = $description;
$widget_modules[$i]['Status'] = 'Enabled';
$i++;
}
// Open modules page and check header.
$this->page->login()->open('zabbix.php?action=module.list');
$this->assertEquals('Modules', $this->query('tag:h1')->one()->getText());
// Check status of buttons on the modules page.
foreach (['Scan directory' => true, 'Enable' => false, 'Disable' => false] as $button => $enabled) {
$this->assertTrue($this->query('button', $button)->one()->isEnabled($enabled));
}
$table = $this->query('class:list-table')->asTable()->one();
// Check that only widget modules are present until the 'Scan directory' button is pressed.
$this->assertTableData($widget_modules);
$count = $table->getRows()->count();
$this->assertTableStats($count);
$this->assertEquals('0 selected', $this->query('id:selected_count')->one()->getText());
// Check modules table headers.
$headers = $table->getHeadersText();
// Remove empty element from headers array.
array_shift($headers);
$this->assertSame(['Name', 'Version', 'Author', 'Description', 'Status'], $headers);
// Load modules.
$this->loadModules();
$all_modules = array_merge($widget_modules, $modules);
$total_count = count($all_modules);
// Sort column contents ascending.
usort($all_modules, function($a, $b) {
return strcmp($a['Name'], $b['Name']);
});
// Check parameters of modules in the modules table.
$this->assertTableData($all_modules);
$count = CDBHelper::getCount('SELECT moduleid FROM module');
$this->assertEquals('Displaying '.$total_count.' of '.$total_count.' found', $this->query('class:table-stats')
->one()->getText()
);
// Load modules again and check that no new modules were added.
$this->loadModules(false);
$this->assertEquals('Displaying '.$count.' of '.$count.' found', $this->query('class:table-stats')->one()->getText());
}
public function getModuleDetails() {
return [
// Module 1.
[
[
'Name' => '1st Module name',
'Version' => '1',
'Author' => '1st Module author',
'Description' => '1st Module description',
'Directory' => 'modules/module_number_1',
'Namespace' => 'Modules\Example_A',
'URL' => '1st module URL',
'Enabled' => false
]
],
// Module 2.
[
[
'Name' => '2nd Module name !@#$%^&*()_+',
'Version' => 'two !@#$%^&*()_+',
'Author' => '2nd Module author !@#$%^&*()_+',
'Description' => 'Module description !@#$%^&*()_+',
'Directory' => 'modules/module_number_2',
'Namespace' => 'Modules\Example_B',
'URL' => '!@#$%^&*()_+',
'Enabled' => false
]
],
// Module 4.
[
[
'Name' => '4th Module',
'Version' => '',
'Author' => '-',
'Description' => '-',
'Directory' => 'modules/module_number_4',
'Namespace' => 'Modules\Example_A',
'URL' => '-',
'Enabled' => false
]
],
// Module 5.
[
[
'Name' => '5th Module',
'Version' => '',
'Author' => '-',
'Description' => 'Adding top-level and sub-level menu',
'Directory' => 'modules/module_number_5',
'Namespace' => 'Modules\Example_E',
'URL' => '-',
'Enabled' => false
]
],
// Clock2.
[
[
'Name' => 'Clock2',
'Version' => '1.1',
'Author' => 'Zabbix QA department',
'Description' => '-',
'Directory' => 'modules/clock32',
'Namespace' => 'Modules\Clock2',
'URL' => '-',
'Enabled' => false
]
],
// Empty widget.
[
[
'Name' => 'Empty widget',
'Version' => '1.0',
'Author' => 'Some Zabbix employee',
'Description' => '-',
'Directory' => 'modules/emptyWidget',
'Namespace' => 'Modules\emptyWidget',
'URL' => '-',
'Enabled' => false
]
],
// Module 6.
[
[
'Name' => 'шестой модуль',
'Version' => 'бета 2',
'Author' => 'Работник Заббикса',
'Description' => 'Удалить "Reports" из меню верхнего уровня, а так же удалить "Maps" из секции "Monitoring".',
'Directory' => 'modules/module_number_6',
'Namespace' => 'Modules\Example_F',
'URL' => '-',
'Enabled' => false
]
]
];
}
/**
* @dataProvider getModuleDetails
* @depends testPageAdministrationGeneralModules_Layout
*/
public function testPageAdministrationGeneralModules_Details($data) {
// Open corresponding module from the modules table.
$this->page->login()->open('zabbix.php?action=module.list');
$this->query('link', $data['Name'])->waitUntilVisible()->one()->click();
$dialog = COverlayDialogElement::find()->one()->waitUntilReady();
$form = $dialog->asForm();
// Check value af every field in Module details form.
foreach ($data as $key => $value) {
$this->assertEquals($value, $form->getFieldContainer($key)->getText());
}
$dialog->close();
}
public function getModuleData() {
return [
// Enable only 1st module - '1st Module' entry added under Monitoring.
[
[
[
'module_name' => '1st Module name',
'menu_entries' => [
[
'name' => '1st Module',
'action' => 'first.module',
'message' => 'If You see this message - 1st module is working'
]
]
]
]
],
// Enable only 2nd Module - '2nd Module' entry added under Monitoring.
[
[
[
'module_name' => '2nd Module name !@#$%^&*()_+',
'menu_entries' => [
[
'name' => '2nd Module',
'action' => 'second.module',
'message' => '2nd module is also working'
]
]
]
]
],
// Enable both 1st and 2nd module - '1st Module' and '2nd Module' entries added under Monitoring.
[
[
[
'module_name' => '1st Module name',
'menu_entries' => [
[
'name' => '1st Module',
'action' => 'first.module',
'message' => 'If You see this message - 1st module is working'
]
]
],
[
'module_name' => '2nd Module name !@#$%^&*()_+',
'menu_entries' => [
[
'name' => '2nd Module',
'action' => 'second.module',
'message' => '2nd module is also working'
]
]
]
]
],
// Attempting to enable two modules that use identical namespace.
[
[
[
'module_name' => '1st Module name',
'menu_entries' => [
[
'name' => '1st Module',
'action' => 'first.module',
'message' => 'If You see this message - 1st module is working'
]
]
],
[
'expected' => TEST_BAD,
'module_name' =>'4th Module',
'menu_entries' => [
[
'name' => '4th Module',
'action' => 'forth.module'
]
],
'error_details' => 'Identical namespace (Modules\Example_A) is used by modules located at '.
'modules/module_number_1, modules/module_number_4.'
]
]
],
// Enable 5th Module - Module 5 menu top level menu is added with 3 entries.
[
[
[
'module_name' => '5th Module',
'top_menu_entry' => 'Module 5 menu',
'menu_entries' => [
[
'name' => 'Your profile',
'action' => 'userprofile.edit',
'message' => 'User profile: Zabbix Administrator',
'check_disabled' => false
],
[
'name' => 'пятый модуль',
'action' => 'fifth.module',
'message' => 'Если ты это читаешь то 5ый модуль работает'
],
[
'name' => 'Module list',
'action' => 'module.list',
'message' => 'Modules',
'check_disabled' => false
]
]
]
]
],
// Enable шестой модуль - Top level menu Reports and menu entry Maps are removed.
[
[
[
'module_name' => 'шестой модуль',
'remove' => true,
'top_menu_entry' => 'Reports',
'menu_entry' => 'Maps'
]
]
]
];
}
/**
* @backupOnce module
* @dataProvider getModuleData
* @depends testPageAdministrationGeneralModules_Layout
*/
public function testPageAdministrationGeneralModules_EnableDisable($data) {
$this->page->login()->open('zabbix.php?action=module.list');
foreach (['list', 'form'] as $view) {
// This block is separate because one of the cases requires one module to be enabled before the other to succeed.
foreach ($data as $module) {
// Enable module and check the success or error message.
$this->enableModule($module, $view);
}
// In case if module should be enabled, check that changes took place and then disable each enabled module.
foreach ($data as $module) {
if (CTestArrayHelper::get($module, 'expected', TEST_GOOD) === TEST_GOOD) {
$this->assertModuleEnabled($module);
$this->disableModule($module, $view);
$this->assertModuleDisabled($module);
}
}
}
}
public function getFilterData() {
return [
// Exact name match.
[
[
'filter' => [
'Name' => '1st Module name'
],
'expected' => [
'1st Module name'
]
]
],
// Partial name match for all 3 modules.
[
[
'filter' => [
'Name' => 'Module'
],
'expected' => [
'1st Module name',
'2nd Module name !@#$%^&*()_+',
'4th Module',
'5th Module'
]
]
],
// Partial name match with space in between.
[
[
'filter' => [
'Name' => 'le n'
],
'expected' => [
'1st Module name',
'2nd Module name !@#$%^&*()_+'
]
]
],
// Filter by various characters in name.
[
[
'filter' => [
'Name' => '!@#$%^&*()_+'
],
'expected' => [
'2nd Module name !@#$%^&*()_+'
]
]
],
// Exact name match with leading and trailing spaces.
[
[
'filter' => [
'Name' => ' 4th Module '
],
'expected' => [
'4th Module'
]
]
],
// Retrieve only Enabled modules.
[
[
'filter' => [
'Status' => 'Enabled'
],
'expected' => array_merge(['2nd Module name !@#$%^&*()_+'], array_keys(self::$widget_descriptions))
]
],
// Retrieve only Disabled modules.
[
[
'filter' => [
'Status' => 'Disabled'
],
'expected' => [
'1st Module name',
'4th Module',
'5th Module',
'Clock2',
'Empty widget',
'шестой модуль'
]
]
],
// Retrieve only Disabled modules that have 'name' string in their name.
[
[
'filter' => [
'Name' => 'name',
'Status' => 'Disabled'
],
'expected' => [
'1st Module name'
]
]
]
];
}
/**
* @dataProvider getFilterData
* @depends testPageAdministrationGeneralModules_Layout
*/
public function testPageAdministrationGeneralModules_Filter($data) {
$this->page->login()->open('zabbix.php?action=module.list');
// Before checking the filter one of the modules needs to be enabled.
$table = $this->query('class:list-table')->asTable()->one();
$row = $table->findRow('Name', '2nd Module name !@#$%^&*()_+');
if ($row->getColumn('Status')->getText() !== 'Enabled') {
$row->query('link:Disabled')->one()->click();
}
// Apply and submit the filter from data provider.
$form = $this->query('name:zbx_filter')->asForm()->one();
$form->fill($data['filter']);
$form->submit();
$this->page->waitUntilReady();
// Check (using module name) that only the expected filters are returned in the list.
$this->assertTableDataColumn(CTestArrayHelper::get($data, 'expected'));
// Reset the filter and check that all loaded modules are displayed.
$this->query('button:Reset')->one()->click();
$count = CDBHelper::getCount('SELECT moduleid FROM module');
$this->assertEquals('Displaying '.$count.' of '.$count.' found', $this->query('class:table-stats')->one()->getText());
}
/**
* @depends testPageAdministrationGeneralModules_Layout
*/
public function testPageAdministrationGeneralModules_SimpleUpdate() {
$sql = 'SELECT * FROM module ORDER BY moduleid';
$initial_hash = CDBHelper::getHash($sql);
// Open one of the modules and update it without making any changes.
$this->page->login()->open('zabbix.php?action=module.list');
$this->query('link:1st Module name')->waitUntilVisible()->one()->click();
$this->page->waitUntilReady();
$this->query('button:Update')->one()->click();
$this->assertMessage(TEST_GOOD, 'Module updated');
// Check that Module has been updated and that there are no changes took place.
$this->assertEquals($initial_hash, CDBHelper::getHash($sql));
}
/**
* @depends testPageAdministrationGeneralModules_Layout
*/
public function testPageAdministrationGeneralModules_Cancel() {
$sql = 'SELECT * FROM module ORDER BY moduleid';
$initial_hash = CDBHelper::getHash($sql);
// Open the module update of which is going to be cancelled.
$this->page->login()->open('zabbix.php?action=module.list');
$this->query('link:1st Module name')->waitUntilVisible()->one()->click();
$this->page->waitUntilReady();
// Edit module status and Cancel the update.
$this->query('id:status')->asCheckbox()->one()->check();
$this->query('button:Cancel')->one()->click();
$this->page->waitUntilReady();
// Check that Module has been updated and that there are no changes took place.
$this->assertEquals($initial_hash, CDBHelper::getHash($sql));
}
public function getWidgetModuleData() {
return [
// Custom widget with JS, css and pre-defined widget type name
[
[
'module_name' => 'Clock2',
'widget_name' => 'Local',
'widget_type' => 'ALARM CLOCK',
'page' => 'Alarm clock page',
'refresh_rate' => '1 minute'
]
],
// Existing default widget.
[
[
'module_name' => 'System information',
'widget_name' => 'System information',
'page' => 'System info page'
]
],
// Existing default widget on which another widget is dependent.
[
[
'module_name' => 'Map navigation tree',
// TODO: Uncomment the below line and delete the line after it when ZBX-22245 will be resolved.
// 'widget_name' => 'Awesome map tree',
'widget_name' => 'Map navigation tree',
'dependent_widget' => 'Map',
'page' => 'Map page'
]
],
// Custom widget with minimal contents.
[
[
'module_name' => 'Empty widget',
'widget_name' => 'Empty widget',
'page' => 'Empty widget page',
'refresh_rate' => '2 minutes'
]
],
// Existing default widget on template dashboard.
[
[
'module_name' => 'Clock',
// TODO: Uncomment the below line and delete the line after it when ZBX-22245 will be resolved.
// 'widget_name' => 'Default clock',
'widget_name' => 'Local',
'template' => true,
'page' => 'Default clock page'
]
],
// Custom widget with minimal contents.
[
[
'module_name' => 'Clock2',
'widget_name' => 'Server',
'widget_type' => 'ALARM CLOCK',
'template' => true,
'not_available' => 'Empty widget',
'page' => 'Alarm clock page'
]
]
];
}
/**
* @onBeforeOnce prepareDashboardData
*
* @depends testPageAdministrationGeneralModules_Layout
*
* @dataProvider getWidgetModuleData
*/
public function testPageAdministrationGeneralModules_ChangeWidgetModuleStatus($module) {
$this->page->login()->open('zabbix.php?action=module.list');
// Determine the original status of the modules to be checked. Scenarios with mixed statuses are not considered.
$initial_status = $this->query('class:list-table')->asTable()->one()->findRow('Name', $module['module_name'])
->getColumn('Status')->getText();
if ($initial_status === 'Disabled') {
$this->enableModule($module, 'list');
$this->checkWidgetModuleStatus($module);
$this->disableModule($module, 'list');
$this->checkWidgetModuleStatus($module, 'disabled');
}
else {
$this->disableModule($module, 'list');
$this->checkWidgetModuleStatus($module, 'disabled');
$this->enableModule($module, 'list');
$this->checkWidgetModuleStatus($module);
}
}
public function getWidgetDimensions() {
return [
// Widget with pre-defined dimensions.
[
[
'module_name' => 'Clock2',
'widget_name' => 'Local',
'widget_type' => 'ALARM CLOCK',
'enable' => true,
'page' => 'Map page',
'dimensions' => ['width: 33.3333%', 'height: 280px']
]
],
// Widget with pre-defined dimensions on template.
[
[
'module_name' => 'Clock2',
'widget_name' => 'Local',
'widget_type' => 'ALARM CLOCK',
'page' => 'Alarm clock page',
'template' => true,
'dimensions' => ['width: 33.3333%', 'height: 280px']
]
],
// Widget with default dimensions.
[
[
'module_name' => 'Empty widget',
'widget_name' => 'Empty widget',
'widget_type' => 'Empty widget',
'enable' => true,
'page' => 'Map page',
'dimensions' => ['width: 50%', 'height: 350px']
]
]
];
}
/**
*
* @depends testPageAdministrationGeneralModules_ChangeWidgetModuleStatus
*
* @dataProvider getWidgetDimensions
*/
public function testPageAdministrationGeneralModules_CheckWidgetDimensions($data) {
$this->page->login();
if (array_key_exists('enable', $data)) {
$this->page->open('zabbix.php?action=module.list');
$this->enableModule($data, 'list');
}
$this->checkWidgetDimensions($data);
// Cancel editing dashboard not to interfere with following cases from data provider.
$this->query('link:Cancel')->one()->click();
}
/**
* Add a widget of a specific type to dashboard or template dashboard and check its default dimensions.
*
* @param array $data data provider.
*/
private function checkWidgetDimensions($data) {
// Open required dashboard page in edit mode.
$url = (array_key_exists('template', $data))
? 'zabbix.php?action=template.dashboard.edit&dashboardid='.self::$template_dashboardid
: 'zabbix.php?action=dashboard.view&dashboardid='.self::$dashboardid;
$this->page->open($url)->waitUntilReady();
$dashboard = CDashboardElement::find()->one()->waitUntilVisible();
$dashboard->selectPage($data['page']);
if (!array_key_exists('template', $data)) {
$dashboard->edit();
}
// Add widget from the data provider.
$widget_form = $dashboard->addWidget()->asForm();
$widget_form->fill(['Type' => CFormElement::RELOADABLE_FILL($data['widget_type'])]);
$widget_form->submit();
// Get widget dimensions from the style attribute of the widget grid element and compare with expected values.
$grid_selector = 'xpath:.//div[contains(@class, "dashboard-grid-widget-head")]/../..';
$widget_dimensions = $dashboard->getWidget($data['widget_name'])->query($grid_selector)->one()->getAttribute('style');
$dimension_array = array_map('trim', explode(';', $widget_dimensions));
foreach ($data['dimensions'] as $dimension) {
$this->assertContains($dimension, $dimension_array);
}
}
/**
* @depends testPageAdministrationGeneralModules_ChangeWidgetModuleStatus
*/
public function testPageAdministrationGeneralModules_DisableAllModules() {
$this->page->login()->open('zabbix.php?action=module.list')->waitUntilReady();
// Disable all modules.
$this->query('id:all_modules')->waitUntilPresent()->asCheckbox()->one()->set(true);
$this->query('button:Disable')->waitUntilCLickable()->one()->click();
$this->page->acceptAlert();
// Wait for the Success message to confirm that modules were disabled before heading to the dashboard.
$this->assertMessage(TEST_GOOD, 'Modules disabled');
// Open dashboard and check that all widgets are inaccessible.
$this->page->open('zabbix.php?action=dashboard.view&dashboardid='.self::$dashboardid)->waitUntilReady();
$this->checkAllWidgetsDisabledOnPage();
// Open template dashboard and check that all widgets are inaccessible.
$this->page->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$template_dashboardid)->waitUntilReady();
$this->checkAllWidgetsDisabledOnPage();
// Open template dashboard on host and check that all widgets are inaccessible.
$this->page->open('zabbix.php?action=host.dashboard.view&hostid='.self::$hostid.'&dashboardid='.self::$template_dashboardid)
->waitUntilReady();
$this->checkAllWidgetsDisabledOnPage();
}
/**
* Check that all widgets that are displayed on opened dashboard page are inaccessible widgets.
*/
private function checkAllWidgetsDisabledOnPage() {
$dashboard = CDashboardElement::find()->one()->waitUntilPresent();
$total_count = $dashboard->getWidgets()->count();
$inaccessible_count = $dashboard->query(self::INACCESSIBLE_XPATH)->waitUntilVisible()->all()->count();
$this->assertEquals($total_count, $inaccessible_count);
}
/**
* Check widgets of the enabled/disabled modules are displayed in dashboards, host dashboard and template dashboard views.
*
* @param array $module module related information from data provider.
* @param string $status status of widget module before execution of this function.
*/
private function checkWidgetModuleStatus($module, $status = 'enabled') {
// Open dashboard or host dashboard and check widget display in this view.
$url = array_key_exists('template', $module)
? 'zabbix.php?action=host.dashboard.view&hostid='.self::$hostid.'&dashboardid='.self::$template_dashboardid
: 'zabbix.php?action=dashboard.view&dashboardid='.self::$dashboardid;
$this->page->open($url)->waitUntilReady();
$dashboard = CDashboardElement::find()->one()->waitUntilVisible();
$this->checkWidgetStatusOnDashboard($dashboard, $module, $status);
// Open Kiosk mode and check widget display again.
$this->checkWidgetStatusOnDashboard($dashboard, $module, $status, 'kiosk');
$this->query('xpath://button[@title="Normal view"]')->one()->click();
$this->page->waitUntilReady();
// Open dashboard in edit mode or open dashboard on template and check widget display again.
if (array_key_exists('template', $module)) {
$this->page->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$template_dashboardid)
->waitUntilReady();
}
else {
$dashboard->edit();
}
$this->checkWidgetStatusOnDashboard($dashboard, $module, $status, 'edit');
// Check that widget is present among widget types dropdown.
$widget_dialog = $dashboard->addWidget();
$widget_type = (array_key_exists('widget_type', $module) ? $module['widget_type'] : $module['module_name']);
$options = $widget_dialog->asForm()->getField('Type')->asDropdown()->getOptions()->asText();
// Check that widget type is present in "Type" dropdown only if corresponding module is enabled.
$this->assertTrue(($status === 'enabled') ? in_array($widget_type, $options) : !in_array($widget_type, $options));
// Check that module that should be present only on regular dashboards is not present (key used only on template).
if (array_key_exists('not_available', $module)) {
$this->assertFalse(in_array($module['not_available'], $options));
}
// Go back to the list of modules after the check is complete.
$widget_dialog->close();
$this->page->open('zabbix.php?action=module.list');
}
/**
* Check enabled or disabled widget display and its parameters on a particular dashboard page.
* Requirements to the widget are dependent on corresponding module status and dashboard mode (view, kiosk, edit modes).
*
* @param CDashboardElement $dashboard dashboard that contains the corresponding module widget.
* @param array $module module related information from data provider.
* @param string $status status of widget module before execution of this function.
* @param string $mode mode of the dashboard.
*/
private function checkWidgetStatusOnDashboard($dashboard, $module, $status, $mode = null) {
$dashboard->selectPage($module['page']);
// Switch to kiosk mode if required.
if ($mode === 'kiosk') {
$this->query('xpath://button[@title="Kiosk mode"]')->one()->click();
$this->page->waitUntilReady();
}
if ($status === 'enabled') {
// Check that widget with required name is shown and that is doesn't have the inaccessilbe widget string in it.
$widget = $dashboard->getWidget($module['widget_name']);
$this->assertFalse($widget->query("xpath:.//div[text()=".CXPathHelper::escapeQuotes(self::INACCESSIBLE_TEXT).
"]")->one(false)->isValid()
);
// Check refresh interval if such specified in the data provider.
if (array_key_exists('refresh_rate', $module) && $mode !== 'edit') {
$this->assertEquals($module['refresh_rate'], $widget->getRefreshInterval());
CPopupMenuElement::find()->one()->close();
}
// Check that dependent widget is there and that it's content is not hidden.
if (array_key_exists('dependent_widget', $module)) {
$dependent_widget = $dashboard->getWidget($module['dependent_widget']);
$this->assertTrue($dependent_widget->isValid());
$this->assertNotEquals(self::INACCESSIBLE_TEXT, $dependent_widget->getContent()->getText());
}
}
else {
// Check that there is only 1 inaccessible widget present on the opened dashboard page.
$this->assertEquals(1, $dashboard->query(self::INACCESSIBLE_XPATH)->waitUntilVisible()->all()->count());
// Get the inaccessible widget and check its contents.
$inaccessible_widget = $dashboard->getWidget('Inaccessible widget');
$this->assertEquals(self::INACCESSIBLE_TEXT, $inaccessible_widget->getContent()->getText());
// Check that withget of the disabled module is not present on the dashboard.
$this->assertFalse($dashboard->getWidget($module['widget_name'], false)->isValid());
// Check that the dependent widget is still there, but its contents is not displayed.
if (array_key_exists('dependent_widget', $module)) {
$dependent_widget = $dashboard->getWidget($module['dependent_widget']);
$this->assertTrue($dependent_widget->isValid());
$this->assertEquals(self::INACCESSIBLE_TEXT, $dependent_widget->getContent()->getText());
}
/**
* Check that edit widget button on disabled module widget is hidden and that it doesn't exist
* if the dashboard is opened in Monitoring => Hosts view (where All hosts link is present) or in kiosk mode.
*/
$edit_button = $inaccessible_widget->query('xpath:.//button['.CXPathHelper::fromClass('js-widget-edit').']');
$this->assertFalse(($mode === 'kiosk' || $this->query('link:All hosts')->one(false)->isValid())
? $edit_button->one(false)->isValid()
: $edit_button->one()->isDisplayed()
);
// It should not be possible only to Delete the widget and only when the dashboard is in edit mode.
$button = $inaccessible_widget->query('xpath:.//button['.CXPathHelper::fromClass('js-widget-action').']')->one();
if ($mode === 'edit') {
$popup_menu = $button->waitUntilPresent()->asPopupButton()->getMenu();
$menu_items = $popup_menu->getItems();
$this->assertEquals(['Copy', 'Paste', 'Delete'], $menu_items->asText());
// Check that inaccessible widgets can only be deleted.
$this->assertEquals(['Delete'], array_values($menu_items->filter(CElementFilter::CLICKABLE)->asText()));
$popup_menu->close();
}
else {
$this->assertFalse($button->isVisible());
}
}
}
/**
* Function loads modules in frontend and checks the message depending on whether new modules were loaded.
*
* @param bool $first_load flag that determines whether modules are loaded for the first time.
*/
private function loadModules($first_load = true) {
// Load modules
$this->query('button:Scan directory')->waitUntilClickable()->one()->click();
$this->page->waitUntilReady();
// Check message after loading modules.
if ($first_load) {
// Each loaded module name is checked separately due to difference in their sorting on Jenkins and locally.
$this->assertMessage(TEST_GOOD, 'Modules updated', ['Modules added:', '1st Module name',
'2nd Module name !@#$%^&*()_+', '4th Module', '5th Module', 'Clock2', 'Empty widget', 'шестой модуль'
]);
}
else {
$this->assertMessage(TEST_GOOD, 'No new modules discovered');
}
}
/**
* Function checks if the corresponding menu entry exists, clicks on it and checks the URL and header of the page.
* If the module should remove a menu entry, the function makes sure that the corresponding menu entry doesn't exist.
*
* @param array $module module related information from data provider.
*/
private function assertModuleEnabled($module) {
$xpath = 'xpath://ul[@class="menu-main"]//a[text()="';
// If module removes a menu entry or top level menu entry, check that such entries are not present.
if (CTestArrayHelper::get($module, 'remove', false)) {
$this->assertEquals(0, $this->query($xpath.$module['menu_entry'].'"]')->count());
if (array_key_exists('top_menu_entry', $module)) {
$this->assertEquals(0, $this->query($xpath.$module['top_menu_entry'].'"]')->count());
}
return;
}
// If module adds single or multiple menu entries, open each corresponding view, check view header and URL.
$top_entry = CTestArrayHelper::get($module, 'top_menu_entry', 'Monitoring');
$this->query('link', $top_entry)->one()->waitUntilClickable()->click();
foreach ($module['menu_entries'] as $entry) {
sleep(1);
$this->query($xpath.$entry['name'].'"]')->one()->waitUntilClickable()->click();
$this->page->waitUntilReady();
$this->assertStringContainsString('zabbix.php?action='.$entry['action'], $this->page->getCurrentURL());
$this->assertEquals($entry['message'], $this->query('tag:h1')->waitUntilVisible()->one()->getText());
}
// Get back to modules list to enable or disable the next module.
$this->page->open('zabbix.php?action=module.list')->waitUntilReady();
}
/**
* Function checks if the corresponding menu entry is removed and url is not active after the module is disabled.
* If enabling the module removes a menu entry, the function checks that it is back after disabling the module.
*
* @param array $module module related information from data provider.
*/
private function assertModuleDisabled($module) {
$xpath = 'xpath://ul[@class="menu-main"]//li/a[text()="';
// If module removes a menu entry or top level menu entry, check that entries are back after disabling the module.
if (array_key_exists('remove', $module)) {
$this->assertEquals(1, $this->query($xpath.$module['menu_entry'].'"]')->count());
if (array_key_exists('top_menu_entry', $module)) {
$this->assertEquals(1, $this->query($xpath.$module['top_menu_entry'].'"]')->count());
}
return;
}
// If module adds single or multiple menu entries, check that entries don't exist after disabling the module.
foreach ($module['menu_entries'] as $entry) {
$check_entry = CTestArrayHelper::get($module, 'top_menu_entry', $entry['name']);
$this->assertEquals(0, $this->query($xpath.$check_entry.'"]')->count());
// In case if module many entry leads to an existing view, don't check that menu entry URL isn't available.
if (CTestArrayHelper::get($entry, 'check_disabled', true)) {
$this->page->open('zabbix.php?action='.$entry['action'])->waitUntilReady();
$message = CMessageElement::find()->one();
$this->assertStringContainsString('Page not found', $message->getText());
$this->page->open('zabbix.php?action=module.list');
}
}
}
/**
* Function enables module from the list in modules page or from module details form, depending on input parameters.
*
* @param array $data data array with module details
* @param string $view view from which the module should be enabled - module list or module details form.
*/
private function enableModule($module, $view) {
$expected = CTestArrayHelper::get($module, 'expected', TEST_GOOD);
// Change module status from Disabled to Enabled.
if ($view === 'form') {
$this->changeModuleStatusFromForm($module['module_name'], true, $expected);
}
else {
$this->changeModuleStatusFromPage($module['module_name'], 'Disabled');
}
// In case of negative test check error message and confirm that module wasn't applied.
if ($expected === TEST_BAD) {
$title = ($view === 'form') ? 'Cannot update module' : 'Cannot enable module';
$this->assertMessage($module['expected'], $title, $module['error_details']);
if ($view === 'form') {
COverlayDialogElement::find()->one()->close();
}
$this->assertModuleDisabled($module);
return;
}
// Check message and confirm that changes, made by the enabled module, took place.
$message = ($view === 'form') ? 'Module updated' : 'Module enabled';
$this->assertMessage($expected, $message);
CMessageElement::find()->one()->close();
}
/**
* Function disables module from the list in modules page or from module details form, depending on input parameters.
*
* @param array $module data array with module details
* @param string $view view from which the module should be enabled - module list or module details form.
*/
private function disableModule($module, $view) {
$expected = CTestArrayHelper::get($module, 'expected', TEST_GOOD);
// In case of negative test do nothing.
if ($expected === TEST_BAD) {
return;
}
// Change module status from Enabled to Disabled.
if ($view === 'form') {
$this->changeModuleStatusFromForm($module['module_name'], false, $expected);
}
else {
$this->changeModuleStatusFromPage($module['module_name'], 'Enabled');
}
// Check message and confirm that changes, made by the module, were reversed.
$message = ($view === 'form') ? 'Module updated' : 'Module disabled';
$this->assertMessage(TEST_GOOD, $message);
}
/**
* Function changes module status from the list in modules page.
*
* @param string $name module name
* @param string $current_status module current status that is going to be changed.
*/
private function changeModuleStatusFromPage($name, $current_status) {
$table = $this->query('class:list-table')->asTable()->one();
$row = $table->findRow('Name', $name);
$row->query('link', $current_status)->one()->click();
$this->page->waitUntilReady();
}
/**
* Function changes module status from the modules details form.
*
* @param string $name module name
* @param bool $enabled boolean value to be set in "Enabled" checkbox in module details form.
* @param constant $expected flag that determines whether the module update should succeed or fail.
*/
private function changeModuleStatusFromForm($name, $enabled, $expected) {
$this->query('link', $name)->waitUntilVisible()->one()->click();
$dialog = COverlayDialogElement::find()->one()->waitUntilReady();
// Edit module status and press update.
$dialog->query('id:status')->asCheckbox()->one()->set($enabled);
$this->query('button:Update')->one()->click();
if ($expected === TEST_GOOD) {
$dialog->ensureNotPresent();
}
}
}