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.

1286 lines
38 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__).'/../behaviors/CMessageBehavior.php';
/**
* @dataSource WebScenarios
*
* @onBefore getContextData
*
* @backup httptest
*/
class testFormWebScenarioStep extends CWebTest {
protected static $templateid;
protected static $update_step = 'step 2 of clone scenario';
const IMPACTLESS_STEP = 'Первый этап вэб сценария';
const HOSTID = 40001;
const TEMPLATE_SCENARIO = 'Template_Web_scenario';
const UPDATE_SCENARIO = 'Scenario for Clone';
const CREATE_SCENARIO = 'Scenario for Update';
const SQL = 'SELECT * FROM httpstep hs INNER JOIN httpstep_field hsf ON hsf.httpstepid = hs.httpstepid';
/**
* Attach MessageBehavior to the test.
*
* @return array
*/
public function getBehaviors() {
return [CMessageBehavior::class];
}
/**
* Get the necessary properties of entities used within this test.
*/
public static function getContextData() {
self::$templateid = CDataHelper::get('WebScenarios.templateid');
}
public static function getStepLayoutData() {
return [
[
[
'context' => 'host'
]
],
[
[
'scenario_name' => self::TEMPLATE_SCENARIO,
'step_name' => 'step 1 of scenario 1',
'context' => 'host'
]
],
[
[
'scenario_name' => self::TEMPLATE_SCENARIO,
'context' => 'template'
]
]
];
}
/**
* @dataProvider getStepLayoutData
*/
public function testFormWebScenarioStep_CheckLayout($data) {
$context_id = ($data['context'] === 'host') ? self::HOSTID : self::$templateid;
$this->page->login()->open('httpconf.php?filter_set=1&filter_hostids%5B0%5D='.$context_id.'&context='.$data['context'])
->waitUntilReady();
// Open the step configuration form for the corresponding web scenario.
$selector = (array_key_exists('scenario_name', $data)) ? $data['scenario_name'] : self::UPDATE_SCENARIO;
$this->query('link', $selector)->waitUntilClickable()->one()->click();
$scenario_form = $this->query('name:webscenario_form')->waitUntilVisible()->asForm()->one();
$scenario_form->selectTab('Steps');
$steps_table = $scenario_form->getField('Steps')->asTable();
// Steps cannot be added to templated web scenario from host, so in this case the existing step is opened.
if (array_key_exists('step_name', $data)) {
$steps_table->findRow('Name', $data['step_name'])->getColumn('Name')->click();
}
else {
$steps_table->query('button:Add')->waitUntilClickable()->one()->click();
}
$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
$step_form = $dialog->asForm();
$title = (array_key_exists('step_name', $data)) ? 'Step of web scenario' : 'New step of web scenario';
$this->assertEquals($title, $dialog->getTitle());
// TODO: xpath quotes should be fixed after git-hook improvements in DEV-2396.
$step_fields = [
'Name' => ['maxlength' => 64],
'id:url' => [],
"xpath:(.//table[@id='step-query-fields']//textarea)[1]" => ['placeholder' => 'name', 'maxlength' => 255],
"xpath:(.//table[@id='step-query-fields']//textarea)[2]" => ['placeholder' => 'value', 'maxlength' => 255],
"xpath:(.//table[@id='step-post-fields']//textarea)[1]" => ['placeholder' => 'name', 'maxlength' => 255],
"xpath:(.//table[@id='step-post-fields']//textarea)[2]" => ['placeholder' => 'value', 'maxlength' => 2000],
'Post type' => ['value' => 'Form data'],
'Raw post' => ['visible' => false],
"xpath:(.//table[@id='step-variables']//textarea)[1]" => ['placeholder' => 'name', 'maxlength' => 255],
"xpath:(.//table[@id='step-variables']//textarea)[2]" => ['placeholder' => 'value', 'maxlength' => 2000],
"xpath:(.//table[@id='step-headers']//textarea)[1]" => ['placeholder' => 'name', 'maxlength' => 255],
"xpath:(.//table[@id='step-headers']//textarea)[2]" => ['placeholder' => 'value', 'maxlength' => 2000],
'Follow redirects' => ['value' => false],
'Retrieve mode' => ['value' => 'Body'],
'Timeout' => ['value' => '15s', 'maxlength' => 255],
'Required string' => ['placeholder' => 'pattern', 'maxlength' => 255],
'Required status codes' => ['maxlength' => 255]
];
// Differences between step creation form and update form of templated scenario step should be taken into account.
if (array_key_exists('step_name', $data)) {
$step_fields['Name'] = ['value' => $data['step_name'], 'enabled' => false, 'maxlength' => 64];
$step_fields['id:url']['value'] = 'http://zabbix.com';
$step_fields['Post type']['value'] = 'Raw data';
$step_fields['Raw post']['visible'] = true;
$step_fields['Follow redirects']['value'] = true;
$initial_type = 'Raw data';
$new_type = 'Form data';
$buttons = ['Parse', 'Update', 'Cancel'];
$post_fields = ['Post type', 'Raw post'];
// TODO: xpath quotes should be fixed after git-hook improvements in DEV-2396.
foreach (['[1]', '[2]'] as $element_index) {
$xpath = "xpath:(.//table[@id='step-post-fields']//textarea)".$element_index;
unset($step_fields[$xpath]);
$this->assertFalse($step_form->query($xpath)->one(false)->isValid());
}
}
else {
$initial_type = 'Form data';
$new_type = 'Raw data';
$buttons = ['Parse', 'Add', 'Cancel'];
$post_fields = ['Post type', 'Post fields'];
}
foreach ($step_fields as $field => $attributes) {
$value = (array_key_exists('value', $attributes)) ? $attributes['value'] : '';
$visible = (array_key_exists('visible', $attributes)) ? $attributes['visible'] : true;
$enabled = (array_key_exists('enabled', $attributes)) ? $attributes['enabled'] : true;
$this->assertEquals($visible, $step_form->getField($field)->isVisible(), $field.' visibility is not '.$visible);
$this->assertEquals($value, $step_form->getField($field)->getValue());
$this->assertTrue($step_form->getField($field)->isEnabled($enabled));
foreach (['maxlength', 'placeholder'] as $attribute) {
if (array_key_exists($attribute, $attributes)) {
$this->assertEquals($attributes[$attribute], $step_form->getField($field)->getAttribute($attribute));
}
}
}
// Check mandatory fields.
$this->assertEquals(['Name', 'URL', 'Timeout'], array_values($step_form->getRequiredLabels()));
// Check Labels in radio buttons.
$radio_buttons = [
'Post type' => ['Form data', 'Raw data'],
'Retrieve mode' => ['Body', 'Headers', 'Body and headers']
];
foreach ($radio_buttons as $radio_button => $labels) {
$this->assertEquals($labels, $step_form->getField($radio_button)->getLabels()->asText());
}
// Check that changing the value in Post type field results in displaying either Post fields or Raw post field.
$field_visibility = [
'Form data' => ['Post fields' => true, 'Raw post' => false],
'Raw data' => ['Post fields' => false, 'Raw post' => true]
];
// Check visibility of the post related fields based on the initially selected post type.
foreach ([$new_type, $initial_type] as $post_type) {
$step_form->getField('Post type')->select($post_type);
foreach ($field_visibility[$post_type] as $post_field => $visible) {
$this->assertEquals($visible, $step_form->getField($post_field)->isDisplayed());
}
}
$mode_field = $step_form->getField('Retrieve mode');
// Check that "Post fields" related fields are disabled only when Retrieve mode is set to Headers.
foreach (['Body and headers' => true, 'Headers' => false, 'Body' => true] as $value => $enabled) {
$mode_field->select($value);
foreach ($post_fields as $post_field) {
/*
* Post fields table has the disabled class even when enabled to disable the drag icon if table has only
* one row. Therefore, the state of all four interactable elements is checked induvidually.
*/
if ($post_field === 'Post fields') {
$post_fields_table = $step_form->getField($post_field);
foreach (['xpath:(.//textarea)[1]', 'xpath:(.//textarea)[2]', 'button:Add', 'button:Remove'] as $element) {
$this->assertTrue($post_fields_table->query($element)->one()->isEnabled($enabled));
}
}
else {
$this->assertTrue($step_form->getField($post_field)->isEnabled($enabled));
}
}
}
// Set Post type to Form data in case of templated web scenario, so that Post field table would be displayed.
if (array_key_exists('step_name', $data)) {
$step_form->getField('Post type')->select('Form data');
}
// Check the layout of table fields.
$table_layout = [
'headers' => ['', 'Name', '', 'Value', '']
];
// Check the layout of the value pair field tables.
foreach (['Query fields', 'Post fields', 'Variables', 'Headers'] as $table_name) {
$table = $step_form->getField($table_name)->asTable();
$row = $table->getRow(0);
$this->assertSame($table_layout['headers'], $table->getHeadersText());
// Check that Add button is clickable and the Remove button is not.
$add_button = $table->query('button:Add')->one();
$this->assertTrue($add_button->isClickable());
$remove_button = $row->query('button:Remove')->one();
$this->assertTrue($remove_button->isClickable());
$remove_button->click();
$this->assertFalse($remove_button->isClickable());
// Check the presence of the draggable icon.
if ($table_name === 'Variables') {
$this->assertFalse($row->query('class:drag-icon')->one(false)->isValid());
}
else {
$drag_icon = $row->query('class:drag-icon')->one();
$this->assertFalse($drag_icon->isEnabled());
}
// Fill in some data in first row and check that Remove buttons and draggable icon became enabled.
foreach(['Name', 'Value'] as $column) {
$row->getColumn($column)->query('xpath:./textarea')->one()->fill('zabbix');
}
$this->assertTrue($row->query('button:Remove')->one()->isClickable());
// Check that draggable icon becomes enabled when a new row is added.
if ($table_name !== 'Variables') {
$this->assertFalse($drag_icon->isEnabled());
$add_button->click();
$this->assertTrue($drag_icon->isEnabled());
}
}
// Check the buttons in the web scenario step coniguration form.
foreach ($buttons as $button) {
$this->assertTrue($dialog->query('button', $button)->one()->isClickable());
}
}
public static function getWebScenarioStepData() {
return [
// #0 Empty name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => '',
'id:url' => 'https://zabbix.com'
],
'error' => 'Incorrect value for field "name": cannot be empty.'
]
],
// #1 Empty space in name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => ' ',
'id:url' => 'https://zabbix.com'
],
'error' => 'Incorrect value for field "name": cannot be empty.'
]
],
// #2 Empty URL.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with empty URL',
'id:url' => ''
],
'error' => 'Incorrect value for field "url": cannot be empty.'
]
],
// #3 Blank space in URL.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with blank space in URL',
'id:url' => ' '
],
'error' => 'Incorrect value for field "url": cannot be empty.'
]
],
// #4 Empty Query field name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with missing query field name',
'id:url' => 'http://zabbix.com'
],
'query fields' => [
[
'name' => '',
'value' => 'query field value'
]
],
'error' => 'Incorrect value for field "query_fields/1/name": cannot be empty.'
]
],
// #5 Empty Post field name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with missing post field name',
'Post type' => 'Form data',
'id:url' => 'http://zabbix.com'
],
'post fields' => [
[
'name' => '',
'value' => 'post field value'
]
],
'error' => 'Incorrect value for field "post_fields/1/name": cannot be empty.'
]
],
// #6 Empty Variables field name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with missing variable name',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => '',
'value' => 'variable field value'
]
],
'error' => 'Incorrect value for field "variables/1/name": cannot be empty.'
]
],
// #7 Variables field name without opening bracket.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with missing variable nameopening bracket',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => 'name}'
]
],
'error' => 'Incorrect value for field "variables/1/name": is not enclosed in {} or is malformed.'
]
],
// #8 Variables field name without closing bracket.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Step with missing variable name closing bracket',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => '{name'
]
],
'error' => 'Incorrect value for field "variables/1/name": is not enclosed in {} or is malformed.'
]
],
// #9 Misplaced brackets in Variables field name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Misplaced brackets in Variables field name',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => '{na}me'
]
],
'error' => 'Incorrect value for field "variables/1/name": is not enclosed in {} or is malformed.'
]
],
// #10 Double brackets in Variables field name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Double brackets in Variables field name',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => '{{name}}'
]
],
'error' => 'Incorrect value for field "variables/1/name": is not enclosed in {} or is malformed.'
]
],
// #11 Only brackets in Variables field name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Only brackets in Variables field name',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => '{}'
]
],
'error' => 'Incorrect value for field "variables/1/name": is not enclosed in {} or is malformed.'
]
],
// #12 Duplicate Variable names.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Duplicate Variable names',
'id:url' => 'http://zabbix.com'
],
'variables' => [
[
'name' => '{name}',
'value' => 'AAA'
],
[
'name' => '{name}',
'value' => 'BBB'
]
],
'error' => 'Incorrect value for field "variables/2": value (name)=({name}) already exists.'
]
],
// #13 Missing Headers name.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Missing Headers name',
'id:url' => 'http://zabbix.com'
],
'headers' => [
[
'name' => '',
'value' => 'AAA'
]
],
'error' => 'Incorrect value for field "headers/1/name": cannot be empty.'
]
],
// #14 Empty timeout.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty timeout',
'id:url' => 'http://zabbix.com',
'Timeout' => ''
],
'error' => 'Incorrect value for field "timeout": cannot be empty.'
]
],
// #15 Empty space in timeout.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty space in timeout',
'id:url' => 'http://zabbix.com',
'Timeout' => ' '
],
'error' => 'Incorrect value for field "timeout": cannot be empty.'
]
],
// #16 Non-numeric timeout.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Non-numeric timeout',
'id:url' => 'http://zabbix.com',
'Timeout' => 'two'
],
'error' => 'Incorrect value for field "timeout": a time unit is expected.'
]
],
// #17 Negative timeout.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Negative timeout',
'id:url' => 'http://zabbix.com',
'Timeout' => '-5s'
],
'error' => 'Incorrect value for field "timeout": a time unit is expected.'
]
],
// #18 Zero timeout.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Zero timeout',
'id:url' => 'http://zabbix.com',
'Timeout' => 0
],
'error' => 'Incorrect value for field "timeout": value must be one of 1-3600.'
]
],
// #19 Too big timeout.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Too big timeout',
'id:url' => 'http://zabbix.com',
'Timeout' => 3601
],
'error' => 'Incorrect value for field "timeout": value must be one of 1-3600.'
]
],
// #20 Too big timeout with suffix.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Too big timeout with suffix',
'id:url' => 'http://zabbix.com',
'Timeout' => '2h'
],
'error' => 'Incorrect value for field "timeout": value must be one of 1-3600.'
]
],
// #21 Non-numeric status code.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Non-numeric status code',
'id:url' => 'http://zabbix.com',
'Required status codes' => 'AAA'
],
'error' => 'Invalid response code "AAA".'
]
],
// #22 Too big status code.
[
[
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Too big status code',
'id:url' => 'http://zabbix.com',
'Required status codes' => '2150000000'
],
'error' => 'Invalid response code "2150000000".'
]
],
// #23 Minimal step configuration.
[
[
'fields' => [
'Name' => 'Min config step',
'id:url' => 'http://zabbix.com'
]
]
],
// #24 Retrieve mode set to Headers - Disabled post fields.
[
[
'fields' => [
'Name' => 'Retrieve headers',
'id:url' => 'http://zabbix.com',
'Retrieve mode' => 'Headers'
]
]
],
// #25 All fields populated.
[
[
'fields' => [
'Name' => 'All possible fields specified',
'id:url' => '良い一日を',
'Retrieve mode' => 'Body and headers',
'Post type' => 'Raw data',
'Raw post' => 'name=良い一日を',
'Follow redirects' => true,
'Timeout' => '33s',
'Required string' => '良い一日を',
'Required status codes' => '200,300,401'
],
'query fields' => [
[
'name' => '1st query name- 良い一日を',
'value' => ''
],
[
'name' => '2nd query name - 良い一日を',
'value' => '2nd query value - 良い一日を'
]
],
'variables' => [
[
'name' => '{1st variable name - 良い一日を}',
'value' => ''
],
[
'name' => '{the 2nd variable name - 良い一日を}',
'value' => '2nd variable value - 良い一日を'
]
],
'headers' => [
[
'name' => '1st header name - 良い一日を',
'value' => '1st header value - 良い一日を'
],
[
'name' => '2nd header name - 良い一日を',
'value' => ''
]
]
]
],
// #26 Maximal allowed length of fields except for timeout since maxlength is 255 but maxvalue is 3600.
[
[
'fields' => [
'Name' => STRING_64,
'id:url' => 'URL has no maxlength',
'Post type' => 'Form data',
'Timeout' => '3600s',
'Required string' => STRING_255,
'Required status codes' => '200'.str_repeat(',200', 63)
],
'query fields' => [
[
'name' => STRING_255,
'value' => STRING_255
],
[
'name' => 'query_name',
'value' => 'query_value'
]
],
'post fields' => [
[
'name' => STRING_255,
'value' => STRING_2000
],
[
'name' => 'post_field_name',
'value' => 'post_field_value'
]
],
'variables' => [
[
'name' => '{'.substr(STRING_255, 0, 253).'}',
'value' => STRING_2000
],
[
'name' => '{variable_name}',
'value' => 'variable_value'
]
],
'headers' => [
[
'name' => STRING_255,
'value' => STRING_2000
],
[
'name' => 'header_name',
'value' => 'header_value'
]
]
]
],
// #27 Trim trailing and leading spaces from step fields.
[
[
'fields' => [
'Name' => ' Step with trailing and leading spaces ',
'id:url' => ' http://zabbix.com' ,
'Timeout' => ' 15 ',
'Required string' => ' Zabbix ',
'Required status codes' => ' 404 '
],
'trim' => true
]
]
];
}
/**
* @dataProvider getWebScenarioStepData
*/
public function testFormWebScenarioStep_Create($data) {
$this->checkStepAction($data, 'create');
}
/**
* @dataProvider getWebScenarioStepData
*/
public function testFormWebScenarioStep_Update($data) {
$this->checkStepAction($data, 'update');
}
/**
* Perform create or update action for a web scenario step and check the result.
*
* @param array $data data provider
* @param string $action action to be performed
*/
protected function checkStepAction($data, $action) {
$expected = CTestArrayHelper::get($data, 'expected', TEST_GOOD);
// Open the web scenario step configuration form in create or update mode depending on the executed action.
$scenario = ($action === 'update') ? self::UPDATE_SCENARIO : self::CREATE_SCENARIO;
$scenario_form = $this->getScenarioFormOnStepsTab($scenario);
$steps_table = $scenario_form->getField('Steps')->asTable();
$open_step = ($action === 'create') ? 'button:Add' : 'link:'.self::$update_step;
$steps_table->query($open_step)->waitUntilClickable()->one()->click();
$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
$step_form = $dialog->asForm();
$step_form->fill($data['fields']);
foreach (['variables', 'query fields', 'post fields', 'headers'] as $field_name) {
if (array_key_exists($field_name, $data)) {
// TODO: Replace the below workaround with the commented line when ZBX-22433 is merged.
// $form->getField(ucfirst($field_name))->asMultifieldTable()->fill($data[$field_name]);
$field = $step_form->getField(ucfirst($field_name));
$this->fillTableField($data[$field_name], $field);
}
}
$step_form->submit();
if ($expected === TEST_BAD) {
// There are step form level errors and scenario form level errors. They are checked differently.
$this->assertMessage(TEST_BAD, 'Cannot '.$action.' web scenario step', $data['error']);
$dialog->close();
}
else {
$scenario_form->submit();
$this->assertMessage(TEST_GOOD, 'Web scenario updated');
// TODO: add logic to trim headers an variables after ZBX-22433 is merged.
if (array_key_exists('trim', $data)) {
$data['fields'] = array_map('trim', $data['fields']);
}
if ($action === 'update') {
self::$update_step = $data['fields']['Name'];
}
$this->query('link', $scenario)->waitUntilClickable()->one()->click();
$this->page->waitUntilReady();
$scenario_form->selectTab('Steps');
// Check values displayed in Steps table.
$step_row = $scenario_form->getField('Steps')->asTable()->findRow('Name', $data['fields']['Name']);
$this->assertEquals($data['fields']['id:url'], $step_row->getColumn('URL')->getText());
$column_mapping = [
'Required' => 'Required string',
'Status codes' => 'Required status codes'
];
foreach (['Timeout' => '15s', 'Required' => '', 'Status codes' => ''] as $column => $value) {
$actual_field = ($column === 'Timeout') ? $column : $column_mapping[$column];
if (array_key_exists($actual_field, $data['fields'])) {
$value = $data['fields'][$actual_field];
}
$this->assertEquals($value, $step_row->getColumn($column)->getText());
}
// Check the values in the web scenario step configuration form.
$step_row->query('link', $data['fields']['Name'])->one()->click();
$step_form->invalidate();
$step_form->checkValue($data['fields']);
foreach (['variables', 'query fields', 'post fields', 'headers'] as $field_name) {
if (array_key_exists($field_name, $data)) {
// TODO: Replace the below workaround with a check via $table->checkValue() when ZBX-22433 is merged.
$field = $step_form->getField(ucfirst($field_name));
$this->checkTableField($field, $data[$field_name]);
}
}
}
}
public function testFormWebScenarioStep_SimpleUpdate() {
$this->checkImpactlessAction('simple_update');
}
public function testFormWebScenarioStep_CancelCreate() {
$this->checkImpactlessAction('cancel_create');
}
public function testFormWebScenarioStep_CancelUpdate() {
$this->checkImpactlessAction('cancel_update');
}
public function testFormWebScenarioStep_CancelDelete() {
$this->checkImpactlessAction('cancel_delete');
}
/**
* Check different action cancellation and update without applying any changes.
*
* @param string $action action to be checked
*/
protected function checkImpactlessAction($action) {
$old_hash = CDBHelper::getHash(self::SQL);
$scenario_form = $this->getScenarioFormOnStepsTab(self::UPDATE_SCENARIO);
$steps = $scenario_form->getField('Steps')->asTable();
if ($action === 'cancel_delete') {
$steps->findRow('Name', self::IMPACTLESS_STEP)->query('button:Remove')->one()->click();
$scenario_form->query('button:Cancel')->one()->click();
$this->page->waitUntilReady();
}
else {
$open_step = ($action === 'cancel_create') ? 'button:Add' : 'link:'.self::IMPACTLESS_STEP;
$steps->query($open_step)->waitUntilClickable()->one()->click();
$dialog = COverlayDialogElement::find()->waitUntilReady()->one();
$step_form = $dialog->asForm();
if ($action === 'simple_update') {
$step_form->submit();
}
else {
$values = [
'Name' => $action.' step',
'id:url' => 'http://'.$action.'.lv',
'Retrieve mode' => 'Headers'
];
$step_form->fill($values);
$dialog->query('button:Cancel')->one()->click();
}
$dialog->ensureNotPresent();
$scenario_form->submit();
$this->page->waitUntilReady();
$this->assertMessage(TEST_GOOD, 'Web scenario updated');
}
$this->assertEquals($old_hash, CDBHelper::getHash(self::SQL));
}
public function testFormWebScenarioStep_Remove() {
$form = $this->getScenarioFormOnStepsTab(self::UPDATE_SCENARIO);
$form->getField('Steps')->asTable()->findRow('Name', self::IMPACTLESS_STEP)->query('button:Remove')->one()->click();
$form->submit();
$this->page->waitUntilReady();
$this->assertMessage(TEST_GOOD, 'Web scenario updated');
$sql = 'SELECT httpstepid FROM httpstep WHERE name='.zbx_dbstr(self::IMPACTLESS_STEP).
' AND httptestid IN (SELECT httptestid FROM httptest WHERE name='.zbx_dbstr(self::UPDATE_SCENARIO).')';
$this->assertEquals(0, CDBHelper::getCount($sql));
}
public static function getStepUrlData() {
return [
// #0 Plain parsing of URL parameters.
[
[
'url' => 'https://intranet.zabbix.com/secure/admin.jspa?login=admin&password=s00p3r%24ecr3%26',
'parsed_query' => [
['name' => 'login', 'value' => 'admin'],
['name' => 'password', 'value' => 's00p3r$ecr3&']
],
'resulting_url' => 'https://intranet.zabbix.com/secure/admin.jspa'
]
],
// #1 Unrelated existing and parsed query parameters.
[
[
'url' => 'https://intranet.zabbix.com/secure/admin.jspa?password=s00p3r%24ecr3%26',
'existing_query' => [
['name' => 'login', 'value' => 'admin']
],
'parsed_query' => [
['name' => 'login', 'value' => 'admin'],
['name' => 'password', 'value' => 's00p3r$ecr3&']
],
'resulting_url' => 'https://intranet.zabbix.com/secure/admin.jspa'
]
],
// #2 Duplicate parameters should not be erased during parsing.
[
[
'url' => 'https://intranet.zabbix.com/secure/admin.jspa?login=user&password=a123%24bcd4%26',
'existing_query' => [
['name' => 'login', 'value' => 'admin'],
['name' => 'login', 'value' => 'user'],
['name' => 'password', 'value' => 'password']
],
'parsed_query' => [
['name' => 'login', 'value' => 'admin'],
['name' => 'login', 'value' => 'user'],
['name' => 'password', 'value' => 'password'],
['name' => 'login', 'value' => 'user'],
['name' => 'password', 'value' => 'a123$bcd4&']
],
'resulting_url' => 'https://intranet.zabbix.com/secure/admin.jspa'
]
],
// #3 Invalid URL part removed during parsing.
[
[
'url' => 'http://www.zabbix.com/enterprise_ready#test',
'parsed_query' => [
['name' => '', 'value' => '']
],
'resulting_url' => 'http://www.zabbix.com/enterprise_ready'
]
],
// #4 Empty query parameter name or value.
[
[
'url' => 'https://intranet.zabbix.com/secure/admin.jspa?=admin&password=',
'parsed_query' => [
['name' => '', 'value' => 'admin'],
['name' => 'password', 'value' => '']
],
'resulting_url' => 'https://intranet.zabbix.com/secure/admin.jspa'
]
],
// #5 Improper query parameter in URL.
[
[
'expected' => TEST_BAD,
'url' => 'http://localhost/zabbix/index.php?test=%11',
'error' => "Failed to parse URL.\n\nURL is not properly encoded."
]
]
];
}
/**
* @dataProvider getStepUrlData
*/
public function testFormWebScenarioStep_ParseUrl($data) {
$step_form = $this->getStepForm(self::UPDATE_SCENARIO);
$url_field = $step_form->getField('id:url');
$url_field->fill($data['url']);
$query_table = $step_form->getField('Query fields');
// Fill in existing query fields if such are present in the data provider.
if (array_key_exists('existing_query', $data)) {
$this->fillTableField($data['existing_query'], $query_table);
}
$step_form->query('button:Parse')->one()->click();
if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_GOOD) {
$query_table->invalidate();
$this->checkTableField($query_table, $data['parsed_query']);
$this->assertEquals($data['resulting_url'], $url_field->getValue());
}
else {
$this->checkErrorDialog($data['error']);
}
}
public static function getStepPostData() {
return [
// #0 Regular conversion to raw data.
[
[
'post' => [
['name' => 'zab bix', 'value' => 'tes&t']
],
'result_raw' => 'zab%20bix=tes%26t'
]
],
// #1 Other languages to raw data.
[
[
'post' => [
['name' => 'тест', 'value' => '自分との戦い いつも負ける']
],
'result_raw' => '%D1%82%D0%B5%D1%81%D1%82=%E8%87%AA%E5%88%86%E3%81%A8%E3%81%AE%E6%88%A6%E3%81%84%20'.
'%E3%81%84%E3%81%A4%E3%82%82%E8%B2%A0%E3%81%91%E3%82%8B'
]
],
// #2 Special symbols to raw data.
[
[
'post' => [
['name' => '!@#$%^&*()', 'value' => '!@#$%^&*()']
],
'result_raw' => '!%40%23%24%25%5E%26*()=!%40%23%24%25%5E%26*()'
]
],
// #3 Converting 2 post fields to raw data.
[
[
'post' => [
['name' => 'zabbix', 'value' => 'test'],
['name' => '&Günter', 'value' => '']
],
'result_raw' => 'zabbix=test&%26G%C3%BCnter'
]
],
// #4 Converting raw data to 2 post fields.
[
[
'raw_data' => 'login=Admin&password={{password}.urlencode()}',
'result_post' => [
['name' => 'login', 'value' => 'Admin'],
['name' => 'password', 'value' => '{{password}.urlencode()}']
]
]
],
// #5 Converting raw data to post fields with encoding and without value.
[
[
'raw_data' => 'log+me+in%24&enter=Sign+in%26',
'result_post' => [
['name' => 'log me in$', 'value' => ''],
['name' => 'enter', 'value' => 'Sign in&']
]
]
],
// #6 Other languages from raw to post fields.
[
[
'raw_data' => '%E0%A4%B9%E0%A4%B0%E0%A4%B5%E0%A4%B2%E0%A5%87=tap%C4%B1ld%C4%B1',
'result_post' => [
['name' => 'हरवले', 'value' => 'tapıldı']
]
]
],
// #7 Missing name from raw data to form data.
[
[
'expected' => TEST_BAD,
'raw_data' => '=value',
'error' => "Cannot convert POST data:\n\nValues without names are not allowed in form fields."
]
],
// #8 Post data validation percent encoding pair is malformed.
[
[
'expected' => TEST_BAD,
'raw_data' => 'test=%11',
'error' => "Cannot convert POST data:\n\nData is not properly encoded."
]
],
// #9 Non-existing character when converting from raw data to post data.
[
[
'expected' => TEST_BAD,
'raw_data' => 'value=%00',
'error' => "Cannot convert POST data:\n\nData is not properly encoded."
]
],
// #10 Unnecessary "=" symbol in raw data when converting to form data.
[
[
'expected' => TEST_BAD,
'raw_data' => 'name=val=ue',
'error' => "Cannot convert POST data:\n\nData is not properly encoded."
]
],
// #11 Non-unicode encodings when converting raw data to post data.
[
[
'expected' => TEST_BAD,
'raw_data' => 'value=%EA%EE%EB%E1%E0%F1%EA%E8',
'error' => "Cannot convert POST data:\n\nURIError: URI malformed"
]
],
// #12 Field name exceeds 255 symbols when converting raw data to post data.
[
[
'expected' => TEST_BAD,
'raw_data' => '123456789012345'.STRING_255,
'error' => "Cannot convert POST data:\n\nName of the form field should not exceed 255 characters."
]
],
// #13 Missing name when converting post field to raw data.
[
[
'expected' => TEST_BAD,
'post' => [
['name' => '', 'value' => '!@#$%^&*()']
],
'error' => "Cannot convert POST data:\n\nValues without names are not allowed in form fields."
]
]
];
}
/**
* @dataProvider getStepPostData
*/
public function testFormWebScenarioStep_ConvertPostData($data) {
$step_form = $this->getStepForm(self::UPDATE_SCENARIO);
$post_type = $step_form->getField('Post type');
if (array_key_exists('raw_data', $data)) {
$post_type->fill('Raw data');
$step_form->getField('Raw post')->fill($data['raw_data']);
$post_type->fill('Form data');
}
else {
$post_table = $step_form->getField('Post fields');
$this->fillTableField($data['post'], $post_table);
$post_type->fill('Raw data');
}
if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_GOOD) {
if (array_key_exists('result_raw', $data)) {
$this->assertEquals($data['result_raw'], $step_form->getField('Raw post')->getValue());
}
else {
$post_fields = $step_form->getField('Post fields');
$this->checkTableField($post_fields, $data['result_post']);
}
}
else {
$this->checkErrorDialog($data['error']);
}
}
/**
* Return web scenario step configuration form for the specified web scenario.
*
* @param string $scenario name of web scenario for which to open step configuration form
*
* @return CFormElement
*/
protected function getStepForm($scenario) {
$scenario_form = $this->getScenarioFormOnStepsTab($scenario);
$scenario_form->getField('Steps')->query('button:Add')->one()->click();
return COverlayDialogElement::find()->waitUntilReady()->one()->asForm();
}
/**
* Return web scenario configuration form with opened Steps tab.
*
* @param string $scenario Name of the scenario to be opened
*
* @return CFormElement
*/
protected function getScenarioFormOnStepsTab($scenario) {
$this->page->login()->open('httpconf.php?filter_set=1&filter_hostids%5B0%5D='.self::HOSTID.'&context=host')
->waitUntilReady();
$this->query('link:'.$scenario)->waitUntilClickable()->one()->click();
$this->page->waitUntilReady();
$scenario_form = $this->query('name:webscenario_form')->asForm()->one();
$scenario_form->selectTab('Steps');
return $scenario_form;
}
/**
* Check the content of the error dialog that appears when parsing URL or converting post data.
*
* @param string $error expected error message text
*/
protected function checkErrorDialog($error) {
$error_dialog = COverlayDialogElement::find()->all()->last()->waitUntilReady();
$this->assertEquals('Error', $error_dialog->getTitle());
$this->assertEquals($error, $error_dialog->getContent()->getText());
$error_dialog->getFooter()->query('button:Ok')->one()->click();
$error_dialog->waitUntilNotPresent();
}
/**
* Compare values from corresponding value pair table with expected data.
*
* @param CElement $table_field value pair field object
* @param array $expected array with reference values
*/
protected function checkTableField($table_field, $expected) {
$obtained_fields = [];
$i = 0;
foreach ($table_field->query('class:form_row')->all() as $table_row) {
$obtained_fields[$i]['name'] = $table_row->query('xpath:(.//textarea)[1]')->one()->getValue();
if (array_key_exists('value', $expected[$i])) {
$obtained_fields[$i]['value'] = $table_row->query('xpath:(.//textarea)[2]')->one()->getValue();
}
$i++;
}
$this->assertEquals($expected, $obtained_fields);
}
/**
* Fill the corresponding value pair table.
*
* @param array $input_data values to be filled in
* @param string $table_field name of the value pair field
*/
protected function fillTableField($input_data, $table_field) {
$count = count($input_data);
$add_button = $table_field->query('button:Add')->one();
$i = 1;
foreach ($input_data as $row) {
// Add row in field table if required.
if ($table_field->query('class:form_row')->all()->count() !== $count) {
$add_button->click();
}
// TODO: xpath quotes should be fixed after git-hook improvements in DEV-2396.
$table_field->query("xpath:(.//tr[".$i."]//textarea)[1]")->one()->fill($row['name']);
if (array_key_exists('value', $row)) {
$table_field->query("xpath:(.//tr[".$i."]//textarea)[2]")->one()->fill($row['value']);
}
$i++;
}
}
}