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

This file contains ambiguous Unicode characters!

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

<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
require_once dirname(__FILE__).'/../../include/CWebTest.php';
require_once dirname(__FILE__).'/../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++;
}
}
}