'13', 'height' => '8']; /** * Attach MessageBehavior to the test. * * @return array */ public function getBehaviors() { return ['class' => CMessageBehavior::class]; } /** * Get all widgets from dashboards with name starting with "Dashboard for Copying widgets". */ public static function getDashboardsData() { static $data = null; if ($data === null) { global $DB; if (!isset($DB['DB'])) { DBconnect($error); } CDataHelper::load('CopyWidgetsDashboards'); $data = CDBHelper::getDataProvider('SELECT w.name, dp.dashboardid FROM widget w'. ' JOIN dashboard_page dp ON w.dashboard_pageid=dp.dashboard_pageid'. ' WHERE dp.dashboardid IN ('. 'SELECT dashboardid FROM dashboard '. 'WHERE name LIKE \'%Dashboard for Copying widgets%\''. ') ORDER BY w.widgetid DESC' ); } return $data; } /** * Get ids for template dashboard cases. */ public static function getTemplatedIds() { self::$templated_dashboardid = CDBHelper::getValue('SELECT dashboardid FROM dashboard WHERE name='. zbx_dbstr(self::TEMPLATED_DASHBOARD_NAME) ); self::$templated_empty_dashboardid = CDBHelper::getValue('SELECT dashboardid FROM dashboard WHERE name='. zbx_dbstr(self::EMPTY_DASHBOARD_NAME) ); self::$modules_dashboardid = CDBHelper::getValue('SELECT dashboardid FROM dashboard WHERE name='. zbx_dbstr(self::MODULES_DASHBOARD_NAME) ); } /** * @dataProvider getDashboardsData */ public function testDashboardCopyWidgets_SameDashboard($data) { $this->copyWidgets($data['dashboardid'], $data['name']); } /** * @backupOnce dashboard * * @dataProvider getDashboardsData */ public function testDashboardCopyWidgets_OtherDashboard($data) { $this->copyWidgets($data['dashboardid'], $data['name'], true); } /** * @dataProvider getDashboardsData */ public function testDashboardCopyWidgets_ReplaceWidget($data) { $this->copyWidgets($data['dashboardid'], $data['name'], true, true); } /** * @dataProvider getDashboardsData */ public function testDashboardCopyWidgets_NewPage($data) { $this->copyWidgets($data['dashboardid'], $data['name'], false, false, true); } /** * Common function for copying widgets testing. * * @param int $start_dashboardid id of a dashboard with widgets for copying * @param string $widget_name name of a widget to be copied * @param boolean $new_dashboard true if the widget is copied to new dashboard, false for the same dashboard * @param boolean $replace true if the widget is being replaced, false if copied to new place * @param boolean $new_page true if the widget is copied to the new page, false if copied to the same page * @param boolean $templated true if it is templated dashboard case, false if regular dashboard */ private function copyWidgets($start_dashboardid, $widget_name, $new_dashboard = false, $replace = false, $new_page = false, $templated = false) { // Exclude Map navigation tree widget from replacing tests. if ($replace && $widget_name === 'Test copy Map navigation tree') { return; } $replaces = self::$replaced_widget_name; // Write name for replacing widget next case. if ($replace) { self::$replaced_widget_name = $widget_name; } // Use the appropriate dashboard and page in case of templated dashboard widgets. if ($templated) { $dashboardid = CDBHelper::getValue('SELECT dashboardid FROM dashboard WHERE name ='. zbx_dbstr(self::TEMPLATED_DASHBOARD_NAME) ); $new_dashboardid = self::$templated_empty_dashboardid; $new_page_name = self::TEMPLATED_PAGE_NAME; $new_pageid = CDBHelper::getValue('SELECT dashboard_pageid FROM dashboard_page WHERE name='. zbx_dbstr(self::TEMPLATED_PAGE_NAME) ); $url = 'zabbix.php?action=template.dashboard.edit&dashboardid='; } else { $dashboardid = $start_dashboardid; $new_dashboardid = CDBHelper::getValue('SELECT dashboardid FROM dashboard WHERE name ='. zbx_dbstr('Dashboard for Paste widgets') ); $new_page_name = self::NEW_PAGE_NAME; $new_pageid = CDBHelper::getValue('SELECT dashboard_pageid FROM dashboard_page WHERE dashboardid ='. $start_dashboardid.' AND name ='.zbx_dbstr(self::NEW_PAGE_NAME) ); $url = 'zabbix.php?action=dashboard.view&dashboardid='; } $this->page->login()->open($url.$dashboardid); $dashboard = CDashboardElement::find()->one(); // Get fields from widget form to compare them with new widget after copying. $widget = $dashboard->getWidget($widget_name)->edit(); $original_form = $widget->getFields()->asValues(); // Get tags of original widget. if (stristr($widget_name, 'Problem')) { $tags = $widget->query('id:tags_table_tags')->asMultifieldTable()->one()->getValue(); } $original_widget_size = $replace ? self::REPLACED_WIDGET_SIZE : CDBHelper::getRow('SELECT w.width, w.height'. ' FROM widget w WHERE EXISTS ('. 'SELECT NULL FROM dashboard_page dp'. ' WHERE w.dashboard_pageid=dp.dashboard_pageid'. ' AND dp.dashboardid='.$dashboardid. ')'. ' AND w.name='.zbx_dbstr($widget_name).' ORDER BY w.widgetid DESC' ); // Close widget configuration overlay. COverlayDialogElement::find()->one()->close(); $dashboard->copyWidget($widget_name); // Open other dashboard for paste widgets. if ($new_dashboard) { $this->page->open($url.$new_dashboardid); $dashboard = CDashboardElement::find()->one(); } if ($new_page) { $dashboard->selectPage($new_page_name); } $dashboard->edit(); if ($replace) { $dashboard->replaceWidget($replaces); } else { $dashboard->pasteWidget(); } // Wait until widget is pasted and loading spinner disappeared. sleep(1); $this->query('xpath://div[contains(@class, "is-loading")]')->waitUntilNotPresent(); $copied_widget = $dashboard->getWidgets()->last(); // For Other dashboard and Map from Navigation tree case - add map source, because it is not being copied by design. if (($new_dashboard || $new_page) && stristr($widget_name, 'Map from tree')) { $copied_widget_form = $copied_widget->edit(); $copied_widget_form->fill(['Filter' => 'Test copy Map navigation tree']); $copied_widget_form->submit(); } $this->assertEquals($widget_name, $copied_widget->getHeaderText()); $copied_fields = $copied_widget->edit()->getFields(); // Check tags of original and copied widget. if (stristr($widget_name, 'Problem')) { $copied_tags = COverlayDialogElement::find()->waitUntilReady()->one()->query('id:tags_table_tags') ->asMultifieldTable()->one()->getValue(); $this->assertEquals($tags, $copied_tags); } $copied_form = $copied_fields->asValues(); $this->assertEquals($original_form, $copied_form); // Close overlay and save dashboard to get new widget size from DB. $copied_overlay = COverlayDialogElement::find()->one(); $copied_overlay->close(); if ($templated) { $this->query('button:Save changes')->one()->click(); } else { $dashboard->save(); } $this->page->waitUntilReady(); // For templated dashboards the below SQL is executed faster than the corresponding record is added to DB. if ($templated) { $this->assertMessage(TEST_GOOD, 'Dashboard updated'); } $copied_widget_size = CDBHelper::getRow('SELECT w.width, w.height'. ' FROM widget w WHERE EXISTS ('. 'SELECT NULL'. ' FROM dashboard_page dp'. ' WHERE w.dashboard_pageid='.($new_page ? $new_pageid : 'dp.dashboard_pageid'). ' AND dp.dashboardid='.($new_dashboard ? $new_dashboardid : $dashboardid). ')'. ' AND w.name='.zbx_dbstr($widget_name).' ORDER BY w.widgetid DESC' ); $this->assertEquals($original_widget_size, $copied_widget_size); } public static function getTemplateDashboardWidgetData() { return [ [ [ 'name' => 'Clock widget', 'copy to' => 'same page' ] ], [ [ 'name' => 'Graph (classic) widget', 'copy to' => 'same page' ] ], [ [ 'name' => 'URL widget', 'copy to' => 'same page' ] ], [ [ 'name' => 'Plain text widget', 'copy to' => 'same page' ] ], [ [ 'name' => 'URL widget', 'copy to' => 'same page' ] ], [ [ 'name' => 'Item value widget', 'copy to' => 'same page' ] ], [ [ 'name' => 'Clock widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'Graph (classic) widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'URL widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'Plain text widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'URL widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'Item value widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'Gauge widget', 'copy to' => 'another page' ] ], [ [ 'name' => 'Clock widget', 'copy to' => 'another dashboard' ] ], [ [ 'name' => 'Graph (classic) widget', 'copy to' => 'another dashboard' ] ], [ [ 'name' => 'URL widget', 'copy to' => 'another dashboard' ] ], [ [ 'name' => 'Plain text widget', 'copy to' => 'another dashboard' ] ], [ [ 'name' => 'URL widget', 'copy to' => 'another dashboard' ] ], [ [ 'name' => 'Item value widget', 'copy to' => 'another dashboard' ] ], [ [ 'name' => 'Clock widget', 'copy to' => 'another template' ] ], [ [ 'name' => 'Gauge widget', 'copy to' => 'another template' ] ] ]; } /** * Function that checks copy operation for template dashboard widgets to different locations. * * @dataProvider getTemplateDashboardWidgetData * * @backupOnce dashboard */ public function testDashboardCopyWidgets_CopyTemplateWidgets($data) { switch ($data['copy to']) { case 'same page': $this->copyWidgets(self::$templated_dashboardid, $data['name'], false, false, false, true); break; case 'another page': $this->copyWidgets(self::$templated_dashboardid, $data['name'], false, false, true, true); break; case 'another dashboard': $this->copyWidgets(self::$templated_dashboardid, $data['name'], true, false, false, true); break; case 'another template': $this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$templated_dashboardid); $dashboard = CDashboardElement::find()->one()->waitUntilVisible(); $dashboard->copyWidget($data['name']); $this->page->open('zabbix.php?action=template.dashboard.edit&templateid=50002'); $this->page->waitUntilReady(); COverlayDialogElement::find()->one()->close(); $this->query('id:dashboard-add')->one()->click(); $this->assertFalse(CPopupMenuElement::find()->one()->getItem('Paste widget')->isEnabled()); $this->closeDialogues(); break; } } public static function getTemplateDashboardPageData() { return [ [ [ 'copy to' => 'same dashboard' ] ], [ [ 'copy to' => 'another dashboard' ] ], [ [ 'copy to' => 'another template' ] ] ]; } /** * Function that checks copy operation for template dashboard pages to different locations. * * @dataProvider getTemplateDashboardPageData */ public function testDashboardCopyWidgets_CopyTemplateDashboardPage($data) { $this->page->login()->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$templated_dashboardid); $dashboard = CDashboardElement::find()->one()->waitUntilVisible(); $dashboard->query('xpath://span[text()= "Page with widgets"]/../button')->one()->click(); CPopupMenuElement::find()->one()->waitUntilVisible()->select('Copy'); switch ($data['copy to']) { case 'same dashboard': $this->query('id:dashboard-add')->one()->click(); CPopupMenuElement::find()->one()->waitUntilVisible()->select('Paste page'); $dashboard->query('xpath:(//span[@title="Page with widgets"])[2]')->waitUntilVisible()->one(); $this->assertEquals(2, $dashboard->query('xpath://span[@title="Page with widgets"]')->all()->count()); break; case 'another dashboard': $this->page->open('zabbix.php?action=template.dashboard.edit&dashboardid='.self::$templated_empty_dashboardid); $this->page->waitUntilReady(); $this->query('id:dashboard-add')->one()->click(); CPopupMenuElement::find()->one()->waitUntilVisible()->select('Paste page'); $this->assertEquals(1, $dashboard->query('xpath://span[@title="Page with widgets"]') ->waitUntilVisible()->all()->count() ); break; case 'another template': $this->page->open('zabbix.php?action=template.dashboard.edit&templateid=50002'); $this->page->waitUntilReady(); COverlayDialogElement::find()->one()->close(); $this->query('id:dashboard-add')->one()->click(); $this->assertFalse(CPopupMenuElement::find()->one()->getItem('Paste page')->isEnabled()); break; } $this->closeDialogues(); } public static function getModuleDashboardWidgetData() { return [ [ [ 'module_name' => 'Action log', 'widget_name' => 'Test copy Action log', 'action' => 'copy widget' ] ], [ [ 'module_name' => 'Graph (classic)', 'widget_name' => 'Test copy classic Graph', 'action' => 'replace', 'target' => 'Test copy Graph prototype' ] ], [ [ 'module_name' => 'Favorite graphs', 'widget_name' => 'Test copy Favorite graphs', 'action' => 'copy page' ] ], [ [ 'module_name' => 'Plain text', 'widget_name' => 'Plain text widget', 'action' => 'copy widget', 'template' => true ] ], [ [ 'module_name' => 'URL', 'widget_name' => 'URL widget', 'action' => 'replace', 'target' => 'Graph prototype widget', 'template' => true ] ], [ [ 'module_name' => 'Item value', 'widget_name' => 'Item value widget', 'action' => 'copy page', 'template' => true ] ] ]; } /** * Function that checks copy of widgets with disabled modules. * * @dataProvider getModuleDashboardWidgetData */ public function testDashboardCopyWidgets_CopyDisabledModuleWidgets($data) { $url = CTestArrayHelper::get($data, 'template') ? 'zabbix.php?action=template.dashboard.edit&dashboardid='.self::$templated_dashboardid : 'zabbix.php?action=dashboard.view&dashboardid='.self::$modules_dashboardid; $this->page->login()->open($url)->waitUntilReady(); $dashboard = CDashboardElement::find()->one()->waitUntilVisible(); // Copy widget or dashboard page. if ($data['action'] === 'copy page') { $page_name = CTestArrayHelper::get($data, 'template') ? 'Page with widgets' : 'Page 1'; $this->query('xpath://span[text()='.CXPathHelper::escapeQuotes($page_name).']/../button') ->waitUntilClickable()->one()->click(); CPopupMenuElement::find()->one()->waitUntilVisible()->select('Copy'); } else { $dashboard->copyWidget($data['widget_name']); } // Disable widget module that corresponds to the copied widget or to one of the widgets on the copied page. $this->page->open('zabbix.php?action=module.list'); $this->query('class:list-table')->asTable()->one()->findRow('Name', $data['module_name']) ->query('link', 'Enabled')->one()->click(); $this->page->waitUntilReady(); $this->assertMessage(TEST_GOOD, 'Module disabled'); // Open dashboard and execute the required action with the disabled module widget. $this->page->open($url)->waitUntilReady(); $dashboard->invalidate(); // Get count of inaccessible widgets. $inaccessible_xpath = 'xpath:.//div[contains(@class, "dashboard-widget-inaccessible")]'; $count = $dashboard->query($inaccessible_xpath)->waitUntilVisible()->count(); // Template dashbards are always in edit mode, so entering edit mode is only required for regular dashboards. if(!array_key_exists('template', $data)) { $dashboard->edit(); } switch ($data['action']) { case 'copy widget': $dashboard->pasteWidget(); // Check that the number on inaccessible widgets is still the same. $this->assertEquals($count, $dashboard->query($inaccessible_xpath)->waitUntilVisible()->count()); break; case 'replace': $dashboard->replaceWidget($data['target']); // Check that the number on inaccessible widgets is still the same. $this->assertEquals($count, $dashboard->query($inaccessible_xpath)->waitUntilVisible()->count()); // Make sure that the target widget is still present $this->assertTrue($dashboard->getWidget($data['target'])->isValid()); break; case 'copy page': $this->query('id:dashboard-add')->one()->click(); CPopupMenuElement::find()->one()->waitUntilVisible()->select('Paste page'); $this->query("xpath:(//span[@title=".CXPathHelper::escapeQuotes($page_name)."])[2]") ->waitUntilVisible()->one(); // Check that no inaccessible widgets are present on the pasted page. $dashboard->selectPage($page_name, 2); $this->assertFalse($dashboard->query($inaccessible_xpath)->one(false)->isValid()); break; } $message = ($data['action'] === 'copy page') ? 'Inaccessible widgets were not pasted.' : 'Cannot paste inaccessible widget.'; $this->assertMessage('warning', $message); // Cancel editing dashboard not to interfere with following cases from data provider. $this->query('link:Cancel')->one()->click(); if ($this->page->isAlertPresent()) { $this->page->acceptAlert(); } } /** * Function that closes all dialogs and alerts on a template dashboard before proceeding to the next test. */ private function closeDialogues() { $overlay = COverlayDialogElement::find()->one(false); if ($overlay->isValid()) { $overlay->close(); } $this->query('link:Cancel')->one()->forceClick(); if ($this->page->isAlertPresent()) { $this->page->acceptAlert(); } } }