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.
1000 lines
33 KiB
1000 lines
33 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';
|
||
|
|
||
|
/**
|
||
|
* @backup hosts
|
||
|
*
|
||
|
* @onBefore prepareProblemsData
|
||
|
*/
|
||
|
class testFormUpdateProblem extends CWebTest {
|
||
|
|
||
|
/**
|
||
|
* Id of the host with problems.
|
||
|
*
|
||
|
* @var integer
|
||
|
*/
|
||
|
protected static $hostid;
|
||
|
|
||
|
/**
|
||
|
* Ids of the triggers for problems.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
protected static $triggerids;
|
||
|
|
||
|
/**
|
||
|
* Time when acknowledge was created.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected static $acktime;
|
||
|
|
||
|
/**
|
||
|
* Attach MessageBehavior to the test.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getBehaviors() {
|
||
|
return [CMessageBehavior::class];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all events related tables hash values.
|
||
|
*/
|
||
|
public static function getHash() {
|
||
|
return CDBHelper::getHash('SELECT * FROM events').
|
||
|
CDBHelper::getHash('SELECT * FROM problem').
|
||
|
CDBHelper::getHash('SELECT * FROM triggers').
|
||
|
CDBHelper::getHash('SELECT * FROM acknowledges').
|
||
|
CDBHelper::getHash('SELECT * FROM event_suppress');
|
||
|
}
|
||
|
|
||
|
public function prepareProblemsData() {
|
||
|
// Create hostgroup for hosts with items triggers.
|
||
|
$hostgroups = CDataHelper::call('hostgroup.create', [['name' => 'Group for Problems Update']]);
|
||
|
$this->assertArrayHasKey('groupids', $hostgroups);
|
||
|
$groupid = $hostgroups['groupids'][0];
|
||
|
|
||
|
// Create host for items and triggers.
|
||
|
$hosts = CDataHelper::call('host.create', [
|
||
|
'host' => 'Host for Problems Update',
|
||
|
'groups' => [['groupid' => $groupid]]
|
||
|
]);
|
||
|
$this->assertArrayHasKey('hostids', $hosts);
|
||
|
self::$hostid = $hosts['hostids'][0];
|
||
|
|
||
|
// Create items on previously created host.
|
||
|
$item_names = ['float', 'char', 'log', 'unsigned', 'text'];
|
||
|
|
||
|
$items_data = [];
|
||
|
foreach ($item_names as $i => $item) {
|
||
|
$items_data[] = [
|
||
|
'hostid' => self::$hostid,
|
||
|
'name' => $item,
|
||
|
'key_' => $item,
|
||
|
'type' => 2,
|
||
|
'value_type' => $i
|
||
|
];
|
||
|
}
|
||
|
|
||
|
$items = CDataHelper::call('item.create', $items_data);
|
||
|
$this->assertArrayHasKey('itemids', $items);
|
||
|
|
||
|
// Create triggers based on items.
|
||
|
$triggers = CDataHelper::call('trigger.create', [
|
||
|
[
|
||
|
'description' => 'Trigger for float',
|
||
|
'expression' => 'last(/Host for Problems Update/float)=0',
|
||
|
'priority' => 0
|
||
|
],
|
||
|
[
|
||
|
'description' => 'Trigger for char',
|
||
|
'expression' => 'last(/Host for Problems Update/char)=0',
|
||
|
'priority' => 1,
|
||
|
'manual_close' => 1
|
||
|
],
|
||
|
[
|
||
|
'description' => 'Trigger for log',
|
||
|
'expression' => 'last(/Host for Problems Update/log)=0',
|
||
|
'priority' => 2
|
||
|
],
|
||
|
[
|
||
|
'description' => 'Trigger for unsigned',
|
||
|
'expression' => 'last(/Host for Problems Update/unsigned)=0',
|
||
|
'priority' => 3
|
||
|
],
|
||
|
[
|
||
|
'description' => 'Trigger for text',
|
||
|
'expression' => 'last(/Host for Problems Update/text)=0',
|
||
|
'priority' => 4
|
||
|
],
|
||
|
[
|
||
|
'description' => 'Trigger for icon test',
|
||
|
'expression' => 'last(/Host for Problems Update/log)=0',
|
||
|
'priority' => 3
|
||
|
]
|
||
|
]);
|
||
|
$this->assertArrayHasKey('triggerids', $triggers);
|
||
|
self::$triggerids = CDataHelper::getIds('description');
|
||
|
|
||
|
// Create events.
|
||
|
$time = time();
|
||
|
$i=0;
|
||
|
foreach (self::$triggerids as $name => $id) {
|
||
|
DBexecute('INSERT INTO events (eventid, source, object, objectid, clock, ns, value, name, severity) VALUES ('.(100550 + $i).', 0, 0, '.
|
||
|
zbx_dbstr($id).', '.$time.', 0, 1, '.zbx_dbstr($name).', '.zbx_dbstr($i).')'
|
||
|
);
|
||
|
$i++;
|
||
|
}
|
||
|
|
||
|
// Create problems.
|
||
|
$j=0;
|
||
|
foreach (self::$triggerids as $name => $id) {
|
||
|
DBexecute('INSERT INTO problem (eventid, source, object, objectid, clock, ns, name, severity) VALUES ('.(100550 + $j).', 0, 0, '.
|
||
|
zbx_dbstr($id).', '.$time.', 0, '.zbx_dbstr($name).', '.zbx_dbstr($j).')'
|
||
|
);
|
||
|
$j++;
|
||
|
}
|
||
|
|
||
|
// Change triggers' state to Problem. Manual close is true for the problem: Trigger for char'.
|
||
|
DBexecute('UPDATE triggers SET value = 1 WHERE description IN ('.zbx_dbstr('Trigger for float').', '.
|
||
|
zbx_dbstr('Trigger for log').', '.zbx_dbstr('Trigger for unsigned').', '.zbx_dbstr('Trigger for text').', '.
|
||
|
zbx_dbstr('Trigger for icon test').')'
|
||
|
);
|
||
|
DBexecute('UPDATE triggers SET value = 1, manual_close = 1 WHERE description = '.zbx_dbstr('Trigger for char'));
|
||
|
|
||
|
// Suppress the problem: 'Trigger for text'.
|
||
|
DBexecute('INSERT INTO event_suppress (event_suppressid, eventid, maintenanceid, suppress_until) VALUES (10050, 100554, NULL, 0)');
|
||
|
|
||
|
// Acknowledge the problem: 'Trigger for unsigned' and get acknowledge time.
|
||
|
CDataHelper::call('event.acknowledge', [
|
||
|
'eventids' => 100553,
|
||
|
'action' => 6,
|
||
|
'message' => 'Acknowledged event'
|
||
|
]);
|
||
|
|
||
|
$event = CDataHelper::call('event.get', [
|
||
|
'eventids' => 100553,
|
||
|
'selectAcknowledges' => ['clock']
|
||
|
]);
|
||
|
self::$acktime = CTestArrayHelper::get($event, '0.acknowledges.0.clock');
|
||
|
}
|
||
|
|
||
|
public function getLayoutData() {
|
||
|
return [
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for float'],
|
||
|
'hintboxes' => [
|
||
|
'Suppress' => 'Manual problem suppression. Date-time input accepts relative and absolute time format.',
|
||
|
'Unsuppress' => 'Deactivates manual suppression.',
|
||
|
'Acknowledge' => 'Confirms the problem is noticed (acknowledging user will be recorded). '.
|
||
|
'Status change triggers action update operation.'
|
||
|
],
|
||
|
'history' => [],
|
||
|
'Acknowledge' => true,
|
||
|
'check_suppress' => true
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for char'],
|
||
|
'close_enabled' => true,
|
||
|
'history' => [],
|
||
|
'Acknowledge' => true
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for text'],
|
||
|
'unsuppress_enabled' => true,
|
||
|
'history' => [],
|
||
|
'Acknowledge' => true
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for unsigned'],
|
||
|
// If problem is Aknowledged - label is changed to Unacknowledge.
|
||
|
'labels' => ['Problem', 'Message', 'History', 'Scope', 'Change severity', 'Suppress',
|
||
|
'Unsuppress', 'Unacknowledge', 'Convert to cause', 'Close problem', ''
|
||
|
],
|
||
|
'message' => 'Acknowledged event',
|
||
|
'Unacknowledge' => true,
|
||
|
'history' => [" Admin (Zabbix Administrator)".
|
||
|
"\nAcknowledged event"
|
||
|
],
|
||
|
'hintboxes' => [
|
||
|
'Suppress' => 'Manual problem suppression. Date-time input accepts relative and absolute time format.',
|
||
|
'Unsuppress' => 'Deactivates manual suppression.',
|
||
|
'Unacknowledge' => 'Undo problem acknowledgement.'
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for log'],
|
||
|
'history' => [],
|
||
|
'Acknowledge' => true
|
||
|
]
|
||
|
],
|
||
|
// Two problems.
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for float', 'Trigger for char'],
|
||
|
// If more than one problems selected - History label is absent.
|
||
|
'labels' => ['Problem', 'Message', 'Scope', 'Change severity', 'Suppress', 'Unsuppress',
|
||
|
'Acknowledge', 'Convert to cause', 'Close problem', ''
|
||
|
],
|
||
|
'close_enabled' => true,
|
||
|
'Acknowledge' => true,
|
||
|
'hintboxes' => [
|
||
|
'Suppress' => 'Manual problem suppression. Date-time input accepts relative and absolute time format.',
|
||
|
'Unsuppress' => 'Deactivates manual suppression.',
|
||
|
'Acknowledge' => 'Confirms the problem is noticed (acknowledging user will be recorded). '.
|
||
|
'Status change triggers action update operation.'
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
// Five problems.
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for float', 'Trigger for char', 'Trigger for log', 'Trigger for unsigned', 'Trigger for text'],
|
||
|
// If more than one problem selected - History label is absent.
|
||
|
'labels' => ['Problem', 'Message', 'Scope', 'Change severity', 'Suppress', 'Unsuppress',
|
||
|
'Acknowledge', 'Unacknowledge', 'Convert to cause', 'Close problem', ''
|
||
|
],
|
||
|
'hintboxes' => [
|
||
|
'Suppress' => 'Manual problem suppression. Date-time input accepts relative and absolute time format.',
|
||
|
'Unsuppress' => 'Deactivates manual suppression.',
|
||
|
'Acknowledge' => 'Confirms the problem is noticed (acknowledging user will be recorded). '.
|
||
|
'Status change triggers action update operation.',
|
||
|
'Unacknowledge' => 'Undo problem acknowledgement.'
|
||
|
],
|
||
|
'close_enabled' => true,
|
||
|
'unsuppress_enabled' => true,
|
||
|
'Acknowledge' => true,
|
||
|
'Unacknowledge' => true
|
||
|
]
|
||
|
]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dataProvider getLayoutData
|
||
|
*/
|
||
|
public function testFormUpdateProblem_Layout($data) {
|
||
|
// Open filtered Problems list.
|
||
|
$this->page->login()->open('zabbix.php?&action=problem.view&filter_set=1&show_suppressed=1&hostids%5B%5D='.self::$hostid)->waitUntilReady();
|
||
|
$table = $this->query('class:list-table')->asTable()->one();
|
||
|
$table->findRows('Problem', $data['problems'])->select();
|
||
|
$this->query('button:Mass update')->waitUntilClickable()->one()->click();
|
||
|
|
||
|
$dialog = COverlayDialogElement::find()->one()->waitUntilReady();
|
||
|
$this->assertEquals('Update problem', $dialog->getTitle());
|
||
|
$form = $dialog->query('id:acknowledge_form')->asForm()->one();
|
||
|
|
||
|
// Check form labels.
|
||
|
$count = count($data['problems']);
|
||
|
$default_labels = ['Problem', 'Message', 'History', 'Scope', 'Change severity', 'Suppress', 'Unsuppress',
|
||
|
'Acknowledge', 'Convert to cause', 'Close problem', ''];
|
||
|
$this->assertEquals(CTestArrayHelper::get($data, 'labels', $default_labels), $form->getLabels()->asText());
|
||
|
|
||
|
// Check "Problem" field value.
|
||
|
$problem = $count > 1 ? $count.' problems selected.' : $data['problems'][0];
|
||
|
$this->assertTrue($form->query('xpath:.//div[@class="wordbreak" and text()='.
|
||
|
CXPathHelper::escapeQuotes($problem).']')->exists()
|
||
|
);
|
||
|
|
||
|
// Check first label in Scope field.
|
||
|
$scope_field = $form->getField('Scope');
|
||
|
$scope_label_query = $count > 1
|
||
|
? 'xpath:.//label[text()="Only selected problems"]/sup[text()='.CXPathHelper::escapeQuotes($count.' events').']'
|
||
|
: 'xpath:.//label[text()="Only selected problem"]';
|
||
|
$this->assertTrue($scope_field->query($scope_label_query)->exists());
|
||
|
|
||
|
// Check second label in Scope field.
|
||
|
$this->assertTrue($scope_field->query("xpath:.//label[text()=".
|
||
|
"\"Selected and all other problems of related triggers\"]/sup[text()=".
|
||
|
CXPathHelper::escapeQuotes($count > 1 ? $count.' events' : '1 event')."]")->exists()
|
||
|
);
|
||
|
|
||
|
// Check Hintboxes.
|
||
|
if (CTestArrayHelper::get($data, 'hintboxes')) {
|
||
|
foreach ($data['hintboxes'] as $field => $text) {
|
||
|
$form->getLabel($field)->query('xpath:./button[@data-hintbox]')->one()->click();
|
||
|
$hint = $this->query('xpath://div[@class="overlay-dialogue"]')->waitUntilPresent()->one();
|
||
|
$this->assertEquals($text, $hint->getText());
|
||
|
$hint->query('class:btn-overlay-close')->waitUntilClickable()->one()->click();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check History field.
|
||
|
if (array_key_exists('history', $data)) {
|
||
|
$history = ($data['history'] === []) ? $data['history'] : [date('Y-m-d H:i:s', self::$acktime).$data['history'][0]];
|
||
|
$history_table = $form->getField('History')->asTable();
|
||
|
$this->assertEquals(['Time', 'User', 'User action', 'Message'], $history_table->getHeadersText());
|
||
|
$this->assertEquals($history, $history_table->getRows()->asText());
|
||
|
|
||
|
if ($data['problems'] === ['Trigger for unsigned']) {
|
||
|
foreach (['Acknowledged', 'Message'] as $icon) {
|
||
|
$this->assertTrue($history_table->query("xpath:.//span[@class=".CXPathHelper::fromClass('icon-action').
|
||
|
" and @title=".CXPathHelper::escapeQuotes($icon)."]")->exists()
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check fields' default values and attributes.
|
||
|
$fields = [
|
||
|
'id:message' => ['value' => '', 'maxlength' => 2048, 'enabled' => true],
|
||
|
'id:scope_0' => ['value' => true, 'enabled' => true], // Only selected problem.
|
||
|
'id:scope_1' => ['value' => false, 'enabled' => true], // Selected and all other problems of related triggers.
|
||
|
'id:change_severity' => ['value' => false, 'enabled' => true],
|
||
|
'id:severity' => ['value' => 'Not classified', 'enabled' => false],
|
||
|
'id:suppress_problem' => ['value' => false, 'enabled' => true],
|
||
|
'id:suppress_time_option' => ['value' => 'Until', 'enabled' => false],
|
||
|
'id:suppress_until_problem' => ['maxlength' => 19, 'value' => 'now+1d', 'enabled' => false, 'placeholder' => 'now+1d'],
|
||
|
'id:unsuppress_problem' => ['value' => false, 'enabled' => CTestArrayHelper::get($data, 'unsuppress_enabled', false)],
|
||
|
'Close problem' => ['value' => false, 'enabled' => CTestArrayHelper::get($data, 'close_enabled', false)]
|
||
|
];
|
||
|
|
||
|
foreach ($fields as $field => $attributes) {
|
||
|
$this->assertEquals($attributes['value'], $form->getField($field)->getValue());
|
||
|
$this->assertTrue($form->getField($field)->isEnabled($attributes['enabled']));
|
||
|
|
||
|
if (array_key_exists('maxlength', $attributes)) {
|
||
|
$this->assertEquals($attributes['maxlength'], $form->getField($field)->getAttribute('maxlength'));
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('placeholder', $attributes)) {
|
||
|
$this->assertEquals($attributes['placeholder'], $form->getField($field)->getAttribute('placeholder'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check default values for 'Acknowledge' and 'Unacknowledge' fields.
|
||
|
foreach (['Acknowledge', 'Unacknowledge'] as $label) {
|
||
|
if (array_key_exists($label, $data)) {
|
||
|
$field = $form->getField($label);
|
||
|
$this->assertEquals(false, $field->getValue());
|
||
|
$this->assertTrue($field->isEnabled());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check Suppress and Unsuppress checkboxes dependency.
|
||
|
if (CTestArrayHelper::get($data, 'unsuppress_enabled')) {
|
||
|
$suppress_combinations = [
|
||
|
['id:suppress_problem', 'id:unsuppress_problem'],
|
||
|
['id:unsuppress_problem', 'id:suppress_problem']
|
||
|
];
|
||
|
|
||
|
foreach ($suppress_combinations as $checkboxes) {
|
||
|
foreach ([true, false] as $state) {
|
||
|
$form->fill([$checkboxes[0] => $state]);
|
||
|
$this->assertTrue($form->getField($checkboxes[1])->isEnabled(!$state));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check other buttons in overlay.
|
||
|
$button_queries = [
|
||
|
// Button ? (help) is covered in testDocumentationLinks.
|
||
|
'xpath:.//button[@title="Close"]' => true,
|
||
|
'xpath:.//button[@id="suppress_until_problem_calendar"]' => false,
|
||
|
'button:Update' => true,
|
||
|
'button:Cancel' => true
|
||
|
];
|
||
|
|
||
|
foreach ($button_queries as $query => $clickable) {
|
||
|
$this->assertEquals($clickable, $dialog->query($query)->one()->isClickable());
|
||
|
}
|
||
|
|
||
|
// Check Suppress field.
|
||
|
if (CTestArrayHelper::get($data, 'check_suppress')) {
|
||
|
$form->fill(['id:suppress_problem' => true]);
|
||
|
|
||
|
// Check Until field is enabled.
|
||
|
$this->assertTrue($form->getField('id:suppress_time_option')->isEnabled());
|
||
|
$this->assertTrue($form->getField('id:suppress_until_problem')->isEnabled());
|
||
|
|
||
|
// Check calendar.
|
||
|
$calendar = $form->query('xpath:.//button[@id="suppress_until_problem_calendar"]')->one();
|
||
|
$calendar->waitUntilClickable()->click();
|
||
|
$calendar_overlay = $this->query('xpath://div[@aria-label="Calendar"]');
|
||
|
$this->assertTrue($calendar_overlay->exists());
|
||
|
$calendar->click();
|
||
|
$this->assertFalse($calendar_overlay->exists());
|
||
|
|
||
|
// Check Until field is disabled.
|
||
|
$form->fill(['id:suppress_time_option' => 'Indefinitely']);
|
||
|
$this->assertFalse($form->getField('id:suppress_until_problem')->isEnabled());
|
||
|
$this->assertEquals(false, $calendar->isClickable());
|
||
|
}
|
||
|
|
||
|
// Check Suppress/Unsuppress fields depending on Close problem checkbox.
|
||
|
if (CTestArrayHelper::get($data, 'close_enabled') && CTestArrayHelper::get($data, 'unsuppress_enabled')) {
|
||
|
foreach ([true, false] as $state) {
|
||
|
$form->fill(['id:close_problem' => $state]);
|
||
|
$this->assertFalse($form->getField('id:suppress_problem')->isEnabled($state));
|
||
|
$this->assertFalse($form->getField('id:unsuppress_problem')->isEnabled($state));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check asterisk text.
|
||
|
$this->assertTrue($form->query('xpath:.//label[@class="form-label-asterisk" and '.
|
||
|
'text()="At least one update operation or message must exist."]')->exists()
|
||
|
);
|
||
|
$dialog->close();
|
||
|
}
|
||
|
|
||
|
public function getFormData() {
|
||
|
return [
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for float'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => '2020-08-01 00:00:00'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "Suppress": invalid time.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for float', 'Trigger for log'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => '2040-08-01'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "Suppress": invalid time.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for text', 'Trigger for log'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => '2040-08-01 00:00:00'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "Suppress": invalid time.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for text'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => '00:00:00'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "suppress_until_problem": a time is expected.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for text'],
|
||
|
'fields' => [
|
||
|
'id:message' => 'not showing message 😾',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+16y'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "Suppress": invalid time.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for text'],
|
||
|
'fields' => [
|
||
|
'id:message' => 'not showing message',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now-1d'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "Suppress": invalid time.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for text'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => '-3d'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "suppress_until_problem": a time is expected.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'expected' => TEST_BAD,
|
||
|
'problems' => ['Trigger for char'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'text'
|
||
|
],
|
||
|
'error' => 'Incorrect value for field "suppress_until_problem": a time is expected.'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for log', 'Trigger for char', 'Trigger for float'],
|
||
|
'fields' => [
|
||
|
'id:scope_1' => true,
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'Information',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+2h'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for log',
|
||
|
'db_fields' => ['message' => '', 'action' => 40, 'new_severity' => 1, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for char',
|
||
|
'db_fields' => ['message' => '', 'action' => 32, 'new_severity' => 0, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for float',
|
||
|
'db_fields' => ['message' => '', 'action' => 40, 'new_severity' => 1, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for float'],
|
||
|
'fields' => [
|
||
|
'id:message' => 'test message text',
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'Warning',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Indefinitely',
|
||
|
'Acknowledge' => true
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for float',
|
||
|
'db_fields' => ['message' => 'test message text', 'action' => 46, 'new_severity' => 2, 'suppress_until' => 0]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for text', 'Trigger for log'],
|
||
|
'fields' => [
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'Not classified',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+30s'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for text',
|
||
|
'db_fields' => ['message' => '', 'action' => 40, 'new_severity' => 0, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for text',
|
||
|
'db_fields' => ['message' => '', 'action' => 40, 'new_severity' => 0, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for char'],
|
||
|
'fields' => [
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'Average',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+3d'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for char',
|
||
|
'db_fields' => ['message' => '', 'action' => 40, 'new_severity' => 3, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for unsigned'],
|
||
|
'fields' => [
|
||
|
'id:message' => '😻 😻 😻',
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'High',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+14y'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for unsigned',
|
||
|
'db_fields' => ['message' => '😻 😻 😻', 'action' => 44, 'new_severity' => 4, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for log'],
|
||
|
'fields' => [
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'Disaster',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+18w'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for log',
|
||
|
'db_fields' => ['message' => '', 'action' => 40, 'new_severity' => 5, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for text', 'Trigger for log'],
|
||
|
'fields' => [
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+9M'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for text',
|
||
|
'db_fields' => ['message' => '', 'action' => 32, 'new_severity' => 0, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for log',
|
||
|
'db_fields' => ['message' => '', 'action' => 32, 'new_severity' => 0, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for log', 'Trigger for char', 'Trigger for float', 'Trigger for text', 'Trigger for unsigned'],
|
||
|
'fields' => [
|
||
|
'id:message' => 'Update all 5 problems',
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'High',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+2h'
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for log',
|
||
|
'db_fields' => ['message' => 'Update all 5 problems', 'action' => 44, 'new_severity' => 4, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for char',
|
||
|
'db_fields' => ['message' => 'Update all 5 problems', 'action' => 44, 'new_severity' => 4, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for float',
|
||
|
'db_fields' => ['message' => 'Update all 5 problems', 'action' => 44, 'new_severity' => 4, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for text',
|
||
|
'db_fields' => ['message' => 'Update all 5 problems', 'action' => 44, 'new_severity' => 4, 'suppress_until' => true]
|
||
|
],
|
||
|
[
|
||
|
'name' => 'Trigger for unsigned',
|
||
|
'db_fields' => ['message' => 'Update all 5 problems', 'action' => 36, 'new_severity' => 0, 'suppress_until' => true]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for text'],
|
||
|
'fields' => [
|
||
|
'id:unsuppress_problem' => true
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for text',
|
||
|
'db_fields' => ['message' => '', 'action' => 64, 'new_severity' => 0, 'suppress_until' => 0]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for unsigned'],
|
||
|
'fields' => [
|
||
|
'Unacknowledge' => true
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for unsigned',
|
||
|
'db_fields' => ['message' => '', 'action' => 16, 'new_severity' => 0, 'suppress_until' => 0]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'problems' => ['Trigger for char'],
|
||
|
'fields' => [
|
||
|
'Close problem' => true
|
||
|
],
|
||
|
'db_check' => [
|
||
|
[
|
||
|
'name' => 'Trigger for char',
|
||
|
'db_fields' => ['message' => '', 'action' => 1, 'new_severity' => 0, 'suppress_until' => 0]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dataProvider getFormData
|
||
|
*/
|
||
|
public function testFormUpdateProblem_Form($data) {
|
||
|
if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
|
||
|
$old_hash = $this->getHash();
|
||
|
}
|
||
|
|
||
|
// Open filtered Problems list.
|
||
|
$this->page->login()->open('zabbix.php?&action=problem.view&show_suppressed=1&hostids%5B%5D='.self::$hostid)->waitUntilReady();
|
||
|
$table = $this->query('class:list-table')->asTable()->one();
|
||
|
|
||
|
$count = count($data['problems']);
|
||
|
$table->findRows('Problem', $data['problems']);
|
||
|
|
||
|
if ($count > 1) {
|
||
|
$table->findRows('Problem', $data['problems'])->select();
|
||
|
$this->query('button:Mass update')->waitUntilClickable()->one()->click();
|
||
|
}
|
||
|
else {
|
||
|
$table->findRow('Problem', $data['problems'][0])->getColumn('Update')->query('tag:a')->waitUntilClickable()->one()->click();
|
||
|
}
|
||
|
|
||
|
$dialog = COverlayDialogElement::find()->one()->waitUntilReady();
|
||
|
$form = $dialog->query('id:acknowledge_form')->asForm()->one();
|
||
|
$form->fill($data['fields']);
|
||
|
$form->submit();
|
||
|
|
||
|
if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
|
||
|
$this->assertTrue($dialog->isVisible());
|
||
|
$this->assertMessage(TEST_BAD, null, $data['error']);
|
||
|
$this->assertEquals($old_hash, $this->getHash());
|
||
|
$dialog->close();
|
||
|
}
|
||
|
else {
|
||
|
$dialog->ensureNotPresent();
|
||
|
$this->page->waitUntilReady();
|
||
|
$this->page->assertHeader('Problems');
|
||
|
|
||
|
$message = ($count > 1) ? 'Events updated' : 'Event updated';
|
||
|
$this->assertMessage(TEST_GOOD, $message);
|
||
|
|
||
|
// Check db change.
|
||
|
foreach ($data['db_check'] as $event) {
|
||
|
$sql = CDBHelper::getRow('SELECT message, action, new_severity, suppress_until'.
|
||
|
' FROM acknowledges'.
|
||
|
' WHERE eventid=('.
|
||
|
'SELECT eventid'.
|
||
|
' FROM events'.
|
||
|
' WHERE name='.zbx_dbstr($event['name']).
|
||
|
') ORDER BY acknowledgeid DESC'
|
||
|
);
|
||
|
|
||
|
// Suppress time is always different, so we check only that it is > 0.
|
||
|
if ($sql['suppress_until'] > 0) {
|
||
|
$sql['suppress_until'] = true;
|
||
|
}
|
||
|
|
||
|
$this->assertEquals($event['db_fields'], $sql);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function getCancelData() {
|
||
|
return [
|
||
|
[
|
||
|
[
|
||
|
'case' => 'Cancel'
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
[
|
||
|
'case' => 'Close'
|
||
|
]
|
||
|
]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @dataProvider getCancelData
|
||
|
*/
|
||
|
public function testFormUpdateProblem_Cancel($data) {
|
||
|
$old_hash = $this->getHash();
|
||
|
|
||
|
// Open filtered Problems list.
|
||
|
$this->page->login()->open('zabbix.php?&action=problem.view&show_suppressed=1&hostids%5B%5D='.self::$hostid)->waitUntilReady();
|
||
|
$this->query('class:list-table')->asTable()->one()->findRow('Problem', 'Trigger for log')->getColumn('Update')
|
||
|
->query('tag:a')->waitUntilClickable()->one()->click();
|
||
|
$dialog = COverlayDialogElement::find()->one()->waitUntilReady();
|
||
|
$dialog->query('id:acknowledge_form')->asForm()->one()->fill([
|
||
|
'id:scope_1' => true,
|
||
|
'id:change_severity' => true,
|
||
|
'id:severity' => 'Disaster',
|
||
|
'id:suppress_problem' => true,
|
||
|
'id:suppress_time_option' => 'Until',
|
||
|
'id:suppress_until_problem' => 'now+2h',
|
||
|
'Acknowledge' => true
|
||
|
]);
|
||
|
|
||
|
$dialog->query(($data['case'] === 'Close') ? 'xpath:.//button[@title="Close"]' : 'button:Cancel')->one()
|
||
|
->waitUntilClickable()->click();
|
||
|
$dialog->ensureNotPresent();
|
||
|
$this->page->assertHeader('Problems');
|
||
|
$this->assertEquals($old_hash, $this->getHash());
|
||
|
}
|
||
|
|
||
|
public function testFormUpdateProblem_CheckSuppressIcon() {
|
||
|
$this->page->login()->open('zabbix.php?&action=problem.view&show_suppressed=1&hostids%5B%5D='.self::$hostid)->waitUntilReady();
|
||
|
$table = $this->query('class:list-table')->asTable()->one();
|
||
|
|
||
|
$row = $table->findRow('Problem', 'Trigger for icon test');
|
||
|
$row->getColumn('Update')->query('tag:a')->waitUntilClickable()->one()->click();
|
||
|
|
||
|
$dialog = COverlayDialogElement::find()->one()->waitUntilReady();
|
||
|
$form = $dialog->query('id:acknowledge_form')->asForm()->one();
|
||
|
$form->fill(['id:suppress_problem' => true, 'id:suppress_time_option' => 'Indefinitely']);
|
||
|
$form->submit();
|
||
|
$dialog->ensureNotPresent();
|
||
|
$this->page->waitUntilReady();
|
||
|
$table->waitUntilReloaded();
|
||
|
|
||
|
// Check suppressed icon and hint.
|
||
|
$this->checkIconAndHint($row, 'zi-eye-off', "Suppressed till: Indefinitely".
|
||
|
"\nManually by: Admin (Zabbix Administrator)"
|
||
|
);
|
||
|
|
||
|
// Suppress the problem in DB: 'Trigger for icon test'.
|
||
|
DBexecute('INSERT INTO event_suppress (event_suppressid, eventid, maintenanceid, suppress_until) VALUES (10051, 100555, NULL, 0)');
|
||
|
|
||
|
// Assert that eye icon stopped blinking.
|
||
|
$this->page->refresh();
|
||
|
$this->assertTrue($row->getColumn('Info')->query('xpath:.//button[not(contains(@class, "js-blink"))]')->exists());
|
||
|
|
||
|
// Unsuppress problem.
|
||
|
$row->getColumn('Update')->query('tag:a')->waitUntilClickable()->one()->click();
|
||
|
$form->fill(['id:unsuppress_problem' => true]);
|
||
|
$form->submit();
|
||
|
$dialog->ensureNotPresent();
|
||
|
$this->page->waitUntilReady();
|
||
|
$table->waitUntilReloaded();
|
||
|
|
||
|
// Check unsuppressed icon and hint.
|
||
|
$this->checkIconAndHint($row, 'zi-eye', 'Unsuppressed by: Admin (Zabbix Administrator)');
|
||
|
|
||
|
// Unsuppress the problem in DB: 'Trigger for icon test'.
|
||
|
DBexecute('DELETE FROM event_suppress WHERE event_suppressid=10051');
|
||
|
$this->page->refresh();
|
||
|
|
||
|
// Check that eye icon disappeared.
|
||
|
$this->assertFalse($row->getColumn('Info')->query("xpath:.//button[@class=".
|
||
|
CXPathHelper::fromClass('zi-eye')."]")->exists()
|
||
|
);
|
||
|
|
||
|
// Check Suppress/Unsuppress icon in History table.
|
||
|
$row->getColumn('Update')->query('tag:a')->waitUntilClickable()->one()->click();
|
||
|
$dialog->waitUntilReady();
|
||
|
$form->invalidate();
|
||
|
$this->checkHistoryTable($form->getField('History')->asTable(), 'User', 'User action');
|
||
|
$dialog->close();
|
||
|
$this->page->waitUntilReady();
|
||
|
|
||
|
// Check Actions hint in Problem row.
|
||
|
$row->invalidate();
|
||
|
$unsuppress_button = 'xpath:.//button['.CXPathHelper::fromClass('zi-eye').']';
|
||
|
$row->getColumn('Actions')->query($unsuppress_button)->waitUntilClickable()->one()->click();
|
||
|
$hint = $this->query('xpath://div[@data-hintboxid and @class="overlay-dialogue"]')->asOverlayDialog()
|
||
|
->one()->waitUntilReady();
|
||
|
$this->checkHistoryTable($hint->query('class:list-table')->asTable()->one(), 'User', 'Action');
|
||
|
$hint->close();
|
||
|
|
||
|
// Check Event details page.
|
||
|
$row->getColumn('Time')->query('tag:a')->waitUntilClickable()->one()->click();
|
||
|
$this->page->assertHeader('Event details');
|
||
|
$this->checkHistoryTable($this->query("xpath://section[@id=\"hat_eventactions\"]//table")->asTable()->one(),
|
||
|
'User/Recipient', 'Action'
|
||
|
);
|
||
|
|
||
|
// Check Actions hint in Event list.
|
||
|
$event_list_table = $this->query('xpath://section[@id="hat_eventlist"]//table')->asTable()->one();
|
||
|
$event_list_table->getRow(0)->getColumn('Actions')->query($unsuppress_button)->waitUntilClickable()->one()->click();
|
||
|
$hint->invalidate();
|
||
|
$this->checkHistoryTable($hint->query('class:list-table')->asTable()->one(), 'User', 'Action');
|
||
|
$hint->close();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function for testing Suppressed/Unsuppressed icon or button in history tables.
|
||
|
*
|
||
|
* @param CTableElement $table problem/event history table
|
||
|
* @param string $user user table header
|
||
|
* @param string $action action table header
|
||
|
*/
|
||
|
private function checkHistoryTable($table, $user, $action) {
|
||
|
// Check last two rows.
|
||
|
foreach ([0, 1] as $i) {
|
||
|
$action_row = $table->getRow($i);
|
||
|
$this->assertEquals('Admin (Zabbix Administrator)', $action_row->getColumn($user)->getText());
|
||
|
$query = ($i === 0)
|
||
|
? 'xpath:.//span[@title="Unsuppressed"]'
|
||
|
: 'xpath:.//*['.CXPathHelper::fromClass('zi-eye-off').']';
|
||
|
$this->assertTrue($action_row->getColumn($action)->query($query)->exists());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param CTableRowElement $row table row where necessary problem is found
|
||
|
* @param string $class suppressed or unsuppressed icon class
|
||
|
* @param string $text text of suppression/unsuppression info-hint
|
||
|
*/
|
||
|
private function checkIconAndHint($row, $class, $text) {
|
||
|
// Assert blinking icon in Info column.
|
||
|
$icon = $row->getColumn('Info')->query('class', [$class, 'js-blink'])->waitUntilVisible();
|
||
|
$this->assertTrue($icon->exists());
|
||
|
|
||
|
// Check icon hintbox.
|
||
|
$icon->one()->waitUntilClickable()->click(true);
|
||
|
$hint = $this->query('xpath://div[@data-hintboxid]')->one();
|
||
|
$this->assertTrue($hint->isVisible());
|
||
|
$this->assertEquals($text, $hint->getText());
|
||
|
$hint->asOverlayDialog()->close();
|
||
|
|
||
|
// Assert non-blinking icon in Actions column.
|
||
|
$this->assertTrue($row->getColumn('Actions')->query('xpath:.//button['.CXPathHelper::fromClass($class).']')->exists());
|
||
|
}
|
||
|
}
|