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.

1224 lines
36 KiB

1 year ago
<?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__).'/../../include/helpers/CDataHelper.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
use Facebook\WebDriver\Exception\UnexpectedAlertOpenException;
/**
* @backup dashboard, hosts
*
* @onBefore prepareTemplateDashboardsData
*/
class testFormTemplateDashboards extends CWebTest {
const UPDATE_TEMPLATEID = 50000; // ID of the "Template ZBX6663 First" template used for template dashboards tests.
const HOST_FOR_TEMPLATE = 99015; // ID of the "Empty host" host to which a template with dashboards will be linked.
protected static $dashboardid_with_widgets;
protected static $empty_dashboardid;
protected static $dashboardid_for_update;
private static $previous_widget_name = 'Widget for update';
/**
* Attach MessageBehavior to the test.
*
* @return array
*/
public function getBehaviors() {
return [
'class' => CMessageBehavior::class
];
}
/**
* Function creates template dashboards and defines the corresponding dashboard IDs.
*/
public static function prepareTemplateDashboardsData() {
$response = CDataHelper::call('templatedashboard.create', [
[
'templateid' => self::UPDATE_TEMPLATEID,
'name' => 'Dashboard with all widgets',
'pages' => [
[
'name' => 'Page with widgets',
'widgets' => [
[
'type' => 'clock',
'name' => 'Clock widget',
'width' => 4,
'height' => 4
],
[
'type' => 'graph',
'name' => 'Graph (classic) widget',
'x' => 4,
'y' => 0,
'width' => 8,
'height' => 4,
'fields' => [
[
'type' => 0,
'name' => 'source_type',
'value' => 1
],
[
'type' => 4,
'name' => 'itemid',
'value' => 400410
]
]
],
[
'type' => 'plaintext',
'name' => 'Plain text widget',
'x' => 12,
'y' => 0,
'width' => 6,
'height' => 4,
'fields' => [
[
'type' => 4,
'name' => 'itemids',
'value' => 400410
]
]
],
[
'type' => 'url',
'name' => 'URL widget',
'x' => 18,
'y' => 0,
'width' => 6,
'height' => 4,
'fields' => [
[
'type' => 1,
'name' => 'url',
'value' => 'http://zabbix.com'
]
]
],
[
'type' => 'graphprototype',
'name' => 'Graph prototype widget',
'x' => 0,
'y' => 4,
'width' => 12,
'height' => 6,
'fields' => [
[
'type' => 7,
'name' => 'graphid',
'value' => 700016
]
]
]
]
]
]
],
[
'templateid' => self::UPDATE_TEMPLATEID,
'name' => 'Empty Dashboard without widgets',
'pages' => [[]]
],
[
'templateid' => self::UPDATE_TEMPLATEID,
'name' => 'Dashboard for widget update',
'pages' => [
[
'widgets' => [
[
'type' => 'clock',
'name' => 'Widget for update',
'x' => 0,
'y' => 0,
'width' => 4,
'height' => 4
],
[
'type' => 'clock',
'name' => 'Widget 4 duplicate check',
'x' => 4,
'y' => 0,
'width' => 4,
'height' => 4
]
]
]
]
]
]);
self::$dashboardid_with_widgets = $response['dashboardids'][0];
self::$empty_dashboardid = $response['dashboardids'][1];
self::$dashboardid_for_update = $response['dashboardids'][2];
}
/**
* Function that links the template with dashboards to "Empty host" host.
*/
public static function prepareHostLinkageToTemplateData() {
CDataHelper::call('host.update', [
'hostid' => self::HOST_FOR_TEMPLATE,
'templates' => [
[
'templateid' => self::UPDATE_TEMPLATEID
]
]
]);
}
public function testFormTemplateDashboards_Layout() {
$this->page->login()->open('zabbix.php?action=template.dashboard.list&templateid='.self::UPDATE_TEMPLATEID);
$this->query('button:Create dashboard')->one()->click();
$this->checkDialogue('Dashboard properties');
// Check the default new dashboard state (title, empty, editable).
$dashboard = CDashboardElement::find()->asDashboard()->one()->waitUntilVisible();
$this->assertEquals('Dashboards', $dashboard->getTitle());
$this->assertTrue($dashboard->isEditable());
$this->assertTrue($dashboard->isEmpty());
$controls = $dashboard->getControls();
$control_buttons = [
'id:dashboard-config', // Dashboard properties menu icon.
'button:Add', // Add widget menu button.
'id:dashboard-add', // Dashboard actions chevron.
'button:Save changes', // Save changes button.
'link:Cancel' // Cancel button.
];
// Check dashboard controls and their corresponding actions.
foreach ($control_buttons as $selector) {
$this->assertTrue($controls->query($selector)->one(false)->isValid());
switch ($selector) {
case 'id:dashboard-config':
$controls->query($selector)->waitUntilClickable()->one()->click();
$this->checkDialogue('Dashboard properties');
break;
case 'id:dashboard-add':
$reference_items = [
'Add widget' => true,
'Add page' => true,
'Paste widget' => false,
'Paste page' => false
];
$controls->query($selector)->one()->click();
$this->checkPopup($reference_items);
break;
case 'button:Add':
$controls->query($selector)->one()->click();
$this->checkDialogue('Add widget');
break;
}
}
// Check breadcrumbs.
foreach (['Hierarchy', 'Content menu'] as $aria_label) {
$this->assertTrue($this->query('xpath://ul[@aria-label='.zbx_dbstr($aria_label).']')->one()->isClickable());
}
// Check the page title and its corresponding actions.
$this->assertEquals(1, $this->query('class:sortable-item')->all()->count());
$page_button = $this->query('class:selected-tab')->one();
$this->assertEquals('Page 1', $page_button->getText());
$page_button->query('xpath:./button')->one()->forceClick();
$page_popup_items = [
'Copy' => true,
'Delete' => false,
'Properties' => true
];
$this->checkPopup($page_popup_items, 'ACTIONS');
// Close the dashboard and corresponding popups so that the next scenario would start without alerts.
$this->closeDialogue();
}
public static function getWidgetLayoutData() {
return [
[
[
'type' => 'Clock',
'fields' => [
[
'name' => 'Name',
'attributes' => [
'placeholder' => 'default',
'maxlength' => 255
]
],
[
'name' => 'Time type',
'type' => 'dropdown',
'possible_values' => ['Local time', 'Server time', 'Host time'],
'value' => 'Local time'
]
]
]
],
[
[
'type' => CFormElement::RELOADABLE_FILL('Graph (classic)'),
'fields' => [
[
'name' => 'Name',
'attributes' => [
'placeholder' => 'default',
'maxlength' => 255
]
],
[
'name' => 'Source',
'type' => 'radio_button',
'possible_values' => ['Graph', 'Simple graph'],
'value' => 'Graph'
],
[
'name' => 'Graph',
'type' => 'multiselect'
],
[
'name' => 'Show legend',
'type' => 'checkbox',
'value' => true
]
]
]
],
[
[
'type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'fields' => [
[
'name' => 'Name',
'attributes' => [
'placeholder' => 'default',
'maxlength' => 255
]
],
[
'name' => 'Source',
'type' => 'radio_button',
'possible_values' => ['Graph prototype', 'Simple graph prototype'],
'value' => 'Graph prototype'
],
[
'name' => 'Graph prototype',
'type' => 'multiselect'
],
[
'name' => 'Show legend',
'type' => 'checkbox',
'value' => true
],
[
'name' => 'Columns',
'value' => 2,
'attributes' => [
'maxlength' => 2
]
],
[
'name' => 'Rows',
'value' => 1,
'attributes' => [
'maxlength' => 2
]
]
]
]
],
[
[
'type' => CFormElement::RELOADABLE_FILL('Plain text'),
'fields' => [
[
'name' => 'Name',
'attributes' => [
'placeholder' => 'default',
'maxlength' => 255
]
],
[
'name' => 'Items',
'type' => 'multiselect'
],
[
'name' => 'Items location',
'type' => 'radio_button',
'possible_values' => ['Left', 'Top'],
'value' => 'Left'
],
[
'name' => 'Show lines',
'value' => 25,
'attributes' => [
'maxlength' => 3
]
],
[
'name' => 'Show text as HTML',
'type' => 'checkbox',
'value' => false
]
]
]
],
[
[
'type' => CFormElement::RELOADABLE_FILL('URL'),
'fields' => [
[
'name' => 'Name',
'attributes' => [
'placeholder' => 'default',
'maxlength' => 255
]
],
[
'name' => 'URL',
'attributes' => [
'maxlength' => 2048
]
]
]
]
]
];
}
/**
* Function that checks the layout and the default settings of widget configuration forms.
*
* @dataProvider getWidgetLayoutData
*/
public function testFormTemplateDashboards_WidgetDefaultLayout($data) {
$this->page->login()->open('zabbix.php?action=template.dashboard.list&templateid='.self::UPDATE_TEMPLATEID);
$this->query('button:Create dashboard')->one()->click();
COverlayDialogElement::find()->one()->waitUntilVisible()->close();
// Select the required type of widget.
$this->query('button:Add')->one()->waitUntilClickable()->click();
$widget_dialog = COverlayDialogElement::find()->asForm()->one()->waitUntilReady();
$widget_dialog->fill(['Type' => $data['type']]);
COverlayDialogElement::find()->waitUntilReady();
// Check form fields and their attributes based on field type.
foreach ($data['fields'] as $field_details) {
$field = $widget_dialog->getField($field_details['name']);
$default_value = CTestArrayHelper::get($field_details, 'value', '');
switch (CTestArrayHelper::get($field_details, 'type', 'input')) {
case 'input':
case 'checkbox':
$this->assertEquals($default_value, $field->getValue());
if (array_key_exists('attributes', $field_details)) {
foreach ($field_details['attributes'] as $attribute => $value) {
$this->assertEquals($value, $field->getAttribute($attribute));
}
}
break;
case 'multiselect':
$default_value = '';
$this->assertEquals($default_value, $field->getValue());
$this->assertEquals('type here to search', $field->query('xpath:.//input')->one()->getAttribute('placeholder'));
break;
case 'dropdown':
$this->assertEquals($default_value, $field->getValue());
$this->assertEquals($field_details['possible_values'], $field->getOptions()->asText());
break;
case 'radio_button':
$this->assertEquals($default_value, $field->getValue());
$this->assertEquals($field_details['possible_values'], $field->getLabels()->asText());
break;
}
}
$this->assertTrue($widget_dialog->getField('Show header')->getValue());
// Close editing dashboard so that next test case would not fail with "Unexpected alert" error.
$this->closeDialogue();
}
public static function getDashboardPropertiesData() {
return [
[
[
'dashboard_properties' => [
'Name' => 'Empty dashboard'
]
]
],
[
[
'expected' => TEST_BAD,
'dashboard_properties' => [
'Name' => ''
],
'error_message' => 'Incorrect value for field "name": cannot be empty.'
]
],
[
[
'expected' => TEST_BAD,
'dashboard_properties' => [
'Name' => ' '
],
'error_message' => 'Incorrect value for field "name": cannot be empty.'
]
],
[
[
'expected' => TEST_BAD,
'dashboard_properties' => [
'Name' => 'Empty Dashboard without widgets'
],
'error_message' => 'Dashboard "Empty Dashboard without widgets" already exists.',
'check_save' => true
]
],
[
[
'dashboard_properties' => [
'Name' => '!@#$%^&*()_+=-09[]{};:\'"',
'Default page display period' => '10 seconds',
'Start slideshow automatically' => true
]
]
],
[
[
'dashboard_properties' => [
'Name' => ' Trailing & leading spaces ',
'Start slideshow automatically' => false
],
'trim' => 'Name'
]
]
];
}
/**
* Function that checks validation of the Dashboard properties overlay dialog when creating a dashboard.
*
* @backupOnce dashboard
*
* @dataProvider getDashboardPropertiesData
*/
public function testFormTemplateDashboards_DashboardPropertiesCreate($data) {
$this->page->login()->open('zabbix.php?action=template.dashboard.list&templateid='.self::UPDATE_TEMPLATEID);
$this->query('button:Create dashboard')->one()->click();
$form = COverlayDialogElement::find()->asForm()->one()->waitUntilVisible();
$form->fill($data['dashboard_properties']);
$old_values = $form->getFields()->filter(new CElementFilter(CElementFilter::VISIBLE))->asValues();
$form->submit();
$this->checkSettings($data, $old_values);
}
/**
* Function that checks validation of Dashboard properties overlay dialog update operations.
*
* @backupOnce dashboard
*
* @dataProvider getDashboardPropertiesData
*/
public function testFormTemplateDashboards_DashboardPropertiesUpdate($data) {
$this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$dashboardid_with_widgets);
$this->query('id:dashboard-config')->one()->waitUntilClickable()->click();
$form = COverlayDialogElement::find()->asForm()->one()->waitUntilVisible();
$form->fill($data['dashboard_properties']);
$old_values = $form->getFields()->filter(new CElementFilter(CElementFilter::VISIBLE))->asValues();
$form->submit();
$this->checkSettings($data, $old_values, 'updated');
}
/**
* Function that checks that no changes occur after saving a template dashboard without changes.
*/
public function testFormTemplateDashboards_SimpleUpdate() {
$sql = 'SELECT * FROM widget w INNER JOIN dashboard_page dp ON dp.dashboard_pageid=w.dashboard_pageid '.
'INNER JOIN dashboard d ON d.dashboardid=dp.dashboardid ORDER BY w.widgetid';
$old_hash = CDBHelper::getHash($sql);
$this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$dashboardid_with_widgets);
$this->query('button:Save changes')->one()->waitUntilClickable()->click();
$this->assertMessage(TEST_GOOD, 'Dashboard updated');
$this->assertEquals($old_hash, CDBHelper::getHash($sql));
}
/**
* Function that checks that no changes occur after cancelling a template dashboard update.
*/
public function testFormTemplateDashboards_Cancel() {
$sql = 'SELECT * FROM widget w INNER JOIN dashboard_page dp ON dp.dashboard_pageid=w.dashboard_pageid '.
'INNER JOIN dashboard d ON d.dashboardid=dp.dashboardid ORDER BY w.widgetid';
$old_hash = CDBHelper::getHash($sql);
$fields = [
'Name' => 'Cancel dashboard update',
'Default page display period' => '10 minutes',
'Start slideshow automatically' => false
];
$this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$dashboardid_with_widgets);
$this->query('id:dashboard-config')->one()->waitUntilClickable()->click();
$form = COverlayDialogElement::find()->asForm()->one()->waitUntilVisible();
$form->fill($fields);
$form->submit();
$this->query('link:Cancel')->one()->waitUntilClickable()->click();
$this->assertEquals($old_hash, CDBHelper::getHash($sql));
}
public static function getWidgetsCreateData() {
return [
// Renaming a widget
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => 'Change widget name'
]
]
],
// Two identical widgets
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => 'Widget 4 duplicate check'
],
'duplicate widget' => true
]
],
// Clock widget with no name
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => ''
]
]
],
// Change time type to Server time
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => 'Clock widget server time',
'Time type' => CFormElement::RELOADABLE_FILL('Server time')
]
]
],
// Change time type to Host time and leave item empty
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => 'Clock widget with Host time no item',
'Time type' => CFormElement::RELOADABLE_FILL('Host time')
],
'error_message' => 'Invalid parameter "Item": cannot be empty.'
]
],
// Change time type to Host time and specify item
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => 'Clock widget with Host time',
'Time type' => CFormElement::RELOADABLE_FILL('Host time'),
'Item' => 'Item ZBX6663 Second'
]
]
],
// Widget with trailing and leading spaces in name
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Clock'),
'Name' => ' Clock widget with trailing and leading spaces '
],
'trim' => 'Name'
]
],
// Graph Classic widget with missing graph
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph (classic)'),
'Name' => 'Graph widget with empty graph',
'Source' => 'Graph',
'Graph' => []
],
'error_message' => 'Invalid parameter "Graph": cannot be empty.'
]
],
// Graph Classic widget with missing item
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph (classic)'),
'Name' => 'Graph widget with empty item',
'Source' => CFormElement::RELOADABLE_FILL('Simple graph'),
'Item' => []
],
'error_message' => 'Invalid parameter "Item": cannot be empty.'
]
],
// Graph Classic widget with graph and legend
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph (classic)'),
'Name' => 'Graph widget with graph and legend',
'Source' => 'Graph',
'Graph' => ['Graph ZBX6663 Second'],
'Show legend' => true
]
]
],
// Graph Classic widget with Simple graph and without legend
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph (classic)'),
'Name' => 'Simple graph without legend',
'Source' => CFormElement::RELOADABLE_FILL('Simple graph'),
'Item' => ['Item ZBX6663 Second'],
'Show legend' => false
]
]
],
// Graph prototype widget with missing graph prototype
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with empty graph',
'Source' => 'Graph prototype',
'Graph prototype' => []
],
'error_message' => 'Invalid parameter "Graph prototype": cannot be empty.'
]
],
// Graph prototype widget with missing item prototype
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with empty item prototype',
'Source' => CFormElement::RELOADABLE_FILL('Simple graph prototype'),
'Item prototype' => []
],
'error_message' => 'Invalid parameter "Item prototype": cannot be empty.'
]
],
// Graph prototype widget with empty Columns parameter
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with empty Columns (dropped to 0)',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Columns' => ''
],
'error_message' => 'Invalid parameter "Columns": value must be one of 1-24.'
]
],
// Graph prototype widget with too high number of Columns
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with too much Columns',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Columns' => 55
],
'error_message' => 'Invalid parameter "Columns": value must be one of 1-24.'
]
],
// Graph prototype widget with negative number of Columns
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with too much Columns',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Columns' => '-5'
],
'error_message' => 'Invalid parameter "Columns": value must be one of 1-24.'
]
],
// Graph prototype widget with missing number of Rows
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with missing Rows (dropped to 0)',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Rows' => ''
],
'error_message' => 'Invalid parameter "Rows": value must be one of 1-16.'
]
],
// Graph prototype widget with number of Rows too high
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with too much rows',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Rows' => 55
],
'error_message' => 'Invalid parameter "Rows": value must be one of 1-16.'
]
],
// Graph prototype widget with negative number of Rows
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget with too much rows',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Rows' => '-5'
],
'error_message' => 'Invalid parameter "Rows": value must be one of 1-16.'
]
],
// Graph prototype widget with graph prototype, legend, 2 rows and 2 columns
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Graph prototype widget legend',
'Source' => 'Graph prototype',
'Graph prototype' => ['GraphPrototype ZBX6663 Second'],
'Show legend' => true,
'Columns' => 2,
'Rows' => 2
]
]
],
// Graph prototype widget with simple graph prototype, without legend, 2 row and 2 column
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Graph prototype'),
'Name' => 'Simple Graph prototype without legend',
'Source' => CFormElement::RELOADABLE_FILL('Simple graph prototype'),
'Item prototype' => ['ItemProto ZBX6663 Second'],
'Show legend' => false,
'Columns' => 1,
'Rows' => 1
]
]
],
// Plain text widget with empty Items parameter
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Plain text'),
'Name' => 'Plain text widget with empty Items',
'Items' => []
],
'error_message' => 'Invalid parameter "Items": cannot be empty.'
]
],
// Plain text widget with empty Show lines parameter (reset to 0)
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Plain text'),
'Name' => 'Plain text widget with empty Show lines',
'Items' => ['Item ZBX6663 Second'],
'Show lines' => ''
],
'error_message' => 'Invalid parameter "Show lines": value must be one of 1-100.'
]
],
// Plain text widget with too high value of Show lines parameter
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Plain text'),
'Name' => 'Plain text widget with too much lines',
'Items' => ['Item ZBX6663 Second'],
'Show lines' => 999
],
'error_message' => 'Invalid parameter "Show lines": value must be one of 1-100.'
]
],
// Plain text widget with negative Show lines parameter
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Plain text'),
'Name' => 'Plain text widget with negative Show lines',
'Items' => ['Item ZBX6663 Second'],
'Show lines' => '-9'
],
'error_message' => 'Invalid parameter "Show lines": value must be one of 1-100.'
]
],
// Plain text widget with Items location = top and text shown as HTML
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Plain text'),
'Name' => 'Plain text widget top location HTML',
'Items' => ['Item ZBX6663 Second'],
'Show lines' => 9,
'Items location' => 'Top',
'Show text as HTML' => true
]
]
],
// Plain text widget with Items location = left and text shown as plain text
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('Plain text'),
'Name' => 'Plain text widget left location no HTML',
'Items' => ['Item ZBX6663 Second'],
'Show lines' => 9,
'Items location' => 'Left',
'Show text as HTML' => false
]
]
],
// URL widget with empty URL
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('URL'),
'Name' => 'URL widget with empty URL'
],
'error_message' => 'Invalid parameter "URL": cannot be empty.'
]
],
// URL widget with incorrect URL
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('URL'),
'Name' => 'URL widget with text URL',
'URL' => 'home_sweet_home'
]
]
],
// URL widget with trailing and leading spaces in URL
[
[
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('URL'),
'Name' => 'URL widget with trailing and leading spaces in URL',
'URL' => ' URL '
],
'trim' => 'URL'
]
],
// URL widget with spaces in URL
[
[
'expected' => TEST_BAD,
'fields' => [
'Type' => CFormElement::RELOADABLE_FILL('URL'),
'Name' => 'URL widget with space in URL',
'URL' => ' '
],
'trim' => 'URL',
'error_message' => 'Invalid parameter "URL": cannot be empty.'
]
]
];
}
/**
* Function that validates the Widget configuration form for a template dashboard.
*
* @dataProvider getWidgetsCreateData
*/
public function testFormTemplateDashboards_CreateWidget($data) {
try {
$this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$empty_dashboardid);
}
catch (UnexpectedAlertOpenException $e) {
// Sometimes previous test leaves dashboard edit page open.
$this->page->acceptAlert();
$this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$empty_dashboardid);
}
$this->query('button:Add')->one()->waitUntilClickable()->click();
$form = COverlayDialogElement::find()->asForm()->one()->waitUntilVisible();
$form->fill($data['fields']);
// Trimming is only triggered together with an on-change event which is generated once focus is removed.
$this->page->removeFocus();
$old_values = $form->getFields()->filter(new CElementFilter(CElementFilter::VISIBLE))->asValues();
$form->submit();
// In case of the scenario with identical widgets the same widget needs to be added once again.
if (array_key_exists('duplicate widget', $data)) {
$this->query('button:Add')->one()->waitUntilClickable()->click();
$form->invalidate();
$form->fill($data['fields']);
$this->page->removeFocus();
$form->submit();
}
$this->checkSettings($data, $old_values, 'updated', 'widget create');
}
/**
* Function that checks update of template dashboards widgets parameters.
*
* @dataProvider getWidgetsCreateData
*/
public function testFormTemplateDashboards_UpdateWidget($data) {
$this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$dashboardid_for_update);
$form = CDashboardElement::find()->one()->getWidget(self::$previous_widget_name)->edit();
COverlayDialogElement::find()->waitUntilReady();
$form->fill($data['fields']);
$this->page->removeFocus();
COverlayDialogElement::find()->waitUntilReady();
$old_values = $form->getFields()->filter(new CElementFilter(CElementFilter::VISIBLE))->asValues();
$form->submit();
$this->checkSettings($data, $old_values, 'updated', 'widget update');
}
/**
* Function that checks the layout of a template dashboard with widgets from monitoring hosts view.
* The ignore browser errors annotation is required due to the errors coming from the URL opened in the URL widget.
*
* @ignoreBrowserErrors
*
* @onBefore prepareHostLinkageToTemplateData
*/
public function testFormTemplateDashboards_ViewDashboardOnHost() {
$this->page->login()->open('zabbix.php?action=host.dashboard.view&hostid='.self::HOST_FOR_TEMPLATE);
$this->page->waitUntilReady();
$this->query('xpath://button[@title="Dashboard list"]')->asPopupButton()->one()
->select('Dashboard with all widgets');
$skip_selectors = [
'class:clock',
'class:flickerfreescreen',
'class:widget-url',
'xpath://footer'
];
$skip_elements = [];
foreach ($skip_selectors as $identifier) {
$skip_elements[] = $this->query($identifier)->waitUntilVisible()->one();
}
$this->assertScreenshotExcept(null, $skip_elements, 'dashboard_on_host');
}
/**
* Functions that checks the layout of the "Dashboard properties" and "Add widget" overlay dialogs.
*
* @param string $title The title of the overlay dialog.
*/
private function checkDialogue($title) {
if ($title === 'Dashboard properties') {
$parameters = [
'Name' => 'New dashboard',
'Default page display period' => '30 seconds',
'Start slideshow automatically' => true
];
$buttons = ['Apply', 'Cancel'];
$display_periods = ['10 seconds', '30 seconds', '1 minute', '2 minutes', '10 minutes', '30 minutes', '1 hour'];
}
else {
$parameters = [
'Show header' => true
];
$buttons = ['Add', 'Cancel'];
}
$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
$form = $dialog->asForm();
$this->assertEquals($title, $dialog->getTitle());
$this->assertEquals(2, $dialog->getFooter()->query('button', $buttons)->all()
->filter(new CElementFilter(CElementFilter::CLICKABLE))->count()
);
foreach ($parameters as $name => $value) {
$this->assertEquals($value, $form->getField($name)->getValue());
}
if ($title === 'Dashboard properties') {
$this->assertEquals($display_periods, $form->getField('Default page display period')->getOptions()->asText());
}
else {
$all_types = ['Action log', 'Clock', 'Discovery status', 'Favorite graphs', 'Favorite maps', 'Gauge', 'Geomap',
'Graph', 'Graph (classic)', 'Graph prototype', 'Host availability', 'Item value', 'Map',
'Map navigation tree', 'Plain text', 'Problem hosts', 'Problems', 'Problems by severity', 'SLA report',
'System information', 'Top hosts', 'Top triggers', 'Trigger overview', 'URL', 'Web monitoring', 'Data overview'
];
$this->assertEquals($all_types, $form->getField('Type')->getOptions()->asText());
}
$dialog->close();
}
/**
* Function that checks the content of popup menu elements on a template dashboard.
*
* @param array $items An array of items and their states in a popup menu.
* @param string $title The title of the popup menu.
*/
private function checkPopup($items, $title = false) {
$popup = CPopupMenuElement::find()->one()->waitUntilVisible();
foreach ($items as $item => $enabled) {
$this->assertTrue($popup->getItem($item)->isEnabled($enabled));
}
if ($title) {
$this->assertEquals([$title], $popup->getTitles()->asText());
}
}
/**
* Function that closes an overlay dialog and alert on a template dashboard before proceeding to the next test.
*/
private function closeDialogue() {
$overlay = COverlayDialogElement::find()->one(false);
if ($overlay->isValid()) {
$overlay->close();
}
$this->query('link:Cancel')->one()->forceClick();
if ($this->page->isAlertPresent()) {
$this->page->acceptAlert();
}
}
/**
* Function that checks the previously saved dashboard settings form.
*
* @param array $data Data provider.
* @param array $old_values Values obtained from the configuration form before saving the dashboard.
* @param string $status Expected successful action that was made to the dashboard after saving it.
* @param string $check Action that should be checked.
*/
private function checkSettings($data, $old_values, $status = 'created', $check = 'dashboard action') {
if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
if (CTestArrayHelper::get($data, 'check_save')) {
$this->query('button:Save changes')->one()->click();
}
else {
if (array_key_exists('trim', $data)) {
$old_values[$data['trim']] = trim($old_values[$data['trim']]);
}
$form = COverlayDialogElement::find()->asForm()->one()->waitUntilVisible();
$this->assertEquals($old_values, $form->getFields()->filter(new CElementFilter(CElementFilter::VISIBLE))->asValues());
}
$this->assertMessage(TEST_BAD, null, $data['error_message']);
$this->closeDialogue();
}
else {
COverlayDialogElement::ensureNotPresent();
// Wait for widgets to be present as dashboard is slow when there ame many widgets on it.
if ($check !== 'dashboard action') {
if (CTestArrayHelper::get($data, 'trim') === 'Name') {
$data['fields']['Name'] = trim($data['fields']['Name']);
}
$name = ($data['fields']['Name'] === '') ? 'Local' : $data['fields']['Name'];
CDashboardElement::find()->waitUntilReady()->one()->getWidget($name);
}
$this->query('button:Save changes')->one()->click();
$this->page->waitUntilReady();
$this->assertMessage(TEST_GOOD, 'Dashboard '.$status);
// In case of successful widget update rewrite the widget name to be updated for the next scenario.
if ($check === 'widget update') {
self::$previous_widget_name = $name;
}
// Trim trailing and leading spaces from reference dashboard name if necessary.
$created_values = $old_values;
if (array_key_exists('trim', $data)) {
$created_values[$data['trim']] = trim($created_values[$data['trim']]);
}
$dashboard_name = ($check === 'dashboard action')
? $created_values['Name']
: (($check === 'widget create') ? 'Empty Dashboard without widgets' : 'Dashboard for widget update');
$this->query('link', $dashboard_name)->one()->waitUntilClickable()->click();
$this->page->waitUntilReady();
if ($check !== 'dashboard action') {
$reopened_form = CDashboardElement::find()->waitUntilReady()->one()->getWidget($name)->edit();
}
else {
$this->query('id:dashboard-config')->one()->click();
$reopened_form = COverlayDialogElement::find()->asForm()->one()->waitUntilVisible();
}
$this->assertEquals($created_values, $reopened_form->getFields()->filter(new CElementFilter(CElementFilter::VISIBLE))->asValues());
$this->closeDialogue();
}
}
}