1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "GTUtilsMdi.h"
23 #include <base_dialogs/MessageBoxFiller.h>
24 #include <drivers/GTKeyboardDriver.h>
25 #include <drivers/GTMouseDriver.h>
26 
27 #include <QApplication>
28 #include <QMainWindow>
29 #include <QMdiArea>
30 #include <QMdiSubWindow>
31 #include <QMenu>
32 
33 #include <U2Core/AppContext.h>
34 #include <U2Core/U2SafePoints.h>
35 
36 #include <U2Gui/MainWindow.h>
37 #include <U2Gui/ObjectViewModel.h>
38 
39 #include "GTGlobals.h"
40 #include "primitives/GTMenu.h"
41 #include "utils/GTThread.h"
42 
43 namespace U2 {
44 using namespace HI;
45 
46 #define GT_CLASS_NAME "GTUtilsMdi"
47 
48 #define GT_METHOD_NAME "click"
click(HI::GUITestOpStatus & os,GTGlobals::WindowAction action)49 void GTUtilsMdi::click(HI::GUITestOpStatus &os, GTGlobals::WindowAction action) {
50     MainWindow *mw = AppContext::getMainWindow();
51     GT_CHECK(mw != nullptr, "MainWindow == NULL");
52 
53     QMainWindow *mainWindow = mw->getQMainWindow();
54     GT_CHECK(mainWindow != nullptr, "QMainWindow == NULL");
55 
56     // TODO: batch tests run fails because of not maximized window by default from settings
57     //    if ((action == GTGlobals::Maximize) || (action == GTGlobals::Minimize)) {
58     //        return;
59     //    }
60 
61 #ifndef Q_OS_DARWIN
62     switch (action) {
63         case GTGlobals::Close: {
64 #    ifdef Q_OS_UNIX
65             GTMenu::clickMainMenuItem(os, QStringList() << "Window"
66                                                         << "Close active view");
67 #    else
68             GTKeyboardDriver::keyPress(Qt::Key_Control);
69             GTKeyboardDriver::keyClick(Qt::Key_F4);
70             GTKeyboardDriver::keyRelease(Qt::Key_Control);
71 #    endif
72             break;
73         }
74         default:
75             GTMenuBar::clickCornerMenu(os, mainWindow->menuBar(), action);
76             break;
77     }
78 #else
79     MWMDIWindow *mdiWindow = mw->getMDIManager()->getActiveWindow();
80     GT_CHECK(mdiWindow != nullptr, "MDIWindow == NULL");
81 
82     // TODO: make click on button
83     switch (action) {
84         case GTGlobals::Maximize:
85             GTWidget::showMaximized(os, mdiWindow);
86             break;
87         case GTGlobals::Close: {
88             int left = mdiWindow->rect().left();
89             int top = mdiWindow->rect().top();
90             QPoint p(left + 15, top - 10);
91             GTMouseDriver::moveTo(mdiWindow->mapToGlobal(p));
92             GTMouseDriver::click();
93             break;
94         }
95         default:
96             assert(false);
97             break;
98     }
99 #endif
100 }
101 #undef GT_METHOD_NAME
102 
103 #define GT_METHOD_NAME "findWindow"
findWindow(HI::GUITestOpStatus & os,const QString & windowName,const GTGlobals::FindOptions & options)104 QWidget *GTUtilsMdi::findWindow(HI::GUITestOpStatus &os, const QString &windowName, const GTGlobals::FindOptions &options) {
105     GT_CHECK_RESULT(!windowName.isEmpty(), "windowname is empty", nullptr);
106 
107     MainWindow *mainWindow = AppContext::getMainWindow();
108     GT_CHECK_RESULT(mainWindow != nullptr, "MainWindow == nullptr", nullptr);
109 
110     for (int time = 0; time < GT_OP_WAIT_MILLIS; time += GT_OP_CHECK_MILLIS) {
111         GTGlobals::sleep(time > 0 ? GT_OP_CHECK_MILLIS : 0);
112 
113         QList<MWMDIWindow *> mdiWindows = mainWindow->getMDIManager()->getWindows();
114         foreach (MWMDIWindow *window, mdiWindows) {
115             QString mdiTitle = window->windowTitle();
116             switch (options.matchPolicy) {
117                 case Qt::MatchExactly:
118                     if (mdiTitle == windowName) {
119                         GTThread::waitForMainThread();
120                         return window;
121                     }
122                     break;
123                 case Qt::MatchContains:
124                     if (mdiTitle.contains(windowName, Qt::CaseInsensitive)) {
125                         GTThread::waitForMainThread();
126                         return window;
127                     }
128                     break;
129                 default:
130                     GT_CHECK_RESULT(false, "Not implemented", nullptr);
131             }
132         }
133         if (!options.failIfNotFound) {
134             break;
135         }
136     }
137 
138     GT_CHECK_RESULT(!options.failIfNotFound, "Widget " + windowName + " not found", nullptr);
139     return nullptr;
140 }
141 #undef GT_METHOD_NAME
142 
143 #define GT_METHOD_NAME "closeActiveWindow"
closeActiveWindow(GUITestOpStatus & os)144 void GTUtilsMdi::closeActiveWindow(GUITestOpStatus &os) {
145     closeWindow(os, activeWindowTitle(os));
146 }
147 #undef GT_METHOD_NAME
148 
149 #define GT_METHOD_NAME "closeWindow"
closeWindow(HI::GUITestOpStatus & os,const QString & windowName,const GTGlobals::FindOptions & options)150 void GTUtilsMdi::closeWindow(HI::GUITestOpStatus &os, const QString &windowName, const GTGlobals::FindOptions &options) {
151     GT_CHECK(windowName.isEmpty() == false, "windowname is empty");
152 
153     MainWindow *mw = AppContext::getMainWindow();
154     GT_CHECK(mw != nullptr, "MainWindow == NULL");
155 
156     MWMDIWindow *window = qobject_cast<MWMDIWindow *>(findWindow(os, windowName, options));
157     GT_CHECK(window != nullptr, "Cannot find MDI window");
158     GTWidget::close(os, window->parentWidget());
159 }
160 #undef GT_METHOD_NAME
161 
162 #define GT_METHOD_NAME "closeAllWindows"
closeAllWindows(HI::GUITestOpStatus & os)163 void GTUtilsMdi::closeAllWindows(HI::GUITestOpStatus &os) {
164 #ifndef Q_OS_DARWIN
165     class Scenario : public CustomScenario {
166     public:
167         void run(HI::GUITestOpStatus &os) {
168             const QList<QMdiSubWindow *> mdiWindows = AppContext::getMainWindow()->getQMainWindow()->findChildren<QMdiSubWindow *>();
169             foreach (QMdiSubWindow *mdiWindow, mdiWindows) {
170                 MessageBoxDialogFiller *filler = new MessageBoxDialogFiller(os, QMessageBox::Discard);
171                 GTUtilsDialog::waitForDialogWhichMayRunOrNot(os, filler);
172                 mdiWindow->close();
173                 GTGlobals::sleep(100);
174                 GTUtilsDialog::removeRunnable(filler);
175             }
176         }
177     };
178 
179     GTThread::runInMainThread(os, new Scenario());
180 #else
181     // GUI on Mac hangs because of bug in QCocoaEventDispatcher
182     // It looks like this issue: https://bugreports.qt.io/browse/QTBUG-45389
183     // This part can be removed after Qt bug will be fixed
184     // And now: some magic!
185 
186     QWidget *prevWindow = nullptr;
187     QWidget *mdiWindow = nullptr;
188     GTGlobals::FindOptions options(false);
189 
190     bool tabbedView = isTabbedLayout(os);
191 
192     while ((mdiWindow = GTUtilsMdi::activeWindow(os, options)) != nullptr) {
193         GT_CHECK(mdiWindow != prevWindow, "Can't close MDI window");
194         prevWindow = mdiWindow;
195 
196         MessageBoxDialogFiller *filler = new MessageBoxDialogFiller(os, QMessageBox::Discard);
197         GTUtilsDialog::waitForDialogWhichMayRunOrNot(os, filler);
198 
199         if (!tabbedView) {
200             QPoint closeButtonPos = GTWidget::getWidgetGlobalTopLeftPoint(os, mdiWindow) + QPoint(10, 5);
201             GTMouseDriver::moveTo(closeButtonPos);
202             GTMouseDriver::click();
203         } else {
204             GTMenu::clickMainMenuItem(os, QStringList() << "Actions"
205                                                         << "Close active view");
206         }
207         GTGlobals::sleep(100);
208         GTThread::waitForMainThread();
209         GTUtilsDialog::removeRunnable(filler);
210     }
211 #endif
212 }
213 #undef GT_METHOD_NAME
214 
215 #define GT_METHOD_NAME "isTabbedLayout"
isTabbedLayout(HI::GUITestOpStatus & os)216 bool GTUtilsMdi::isTabbedLayout(HI::GUITestOpStatus &os) {
217     MainWindow *mainWindow = AppContext::getMainWindow();
218     GT_CHECK_RESULT(mainWindow != nullptr, "MainWindow == NULL", false);
219     QMdiArea *mdiArea = GTWidget::findExactWidget<QMdiArea *>(os, "MDI_Area", mainWindow->getQMainWindow());
220     GT_CHECK_RESULT(mdiArea != nullptr, "mdiArea == NULL", false);
221     return mdiArea->viewMode() == QMdiArea::TabbedView;
222 }
223 #undef GT_METHOD_NAME
224 
225 #define GT_METHOD_NAME "activeWindow"
activeWindow(HI::GUITestOpStatus & os,const GTGlobals::FindOptions & options)226 QWidget *GTUtilsMdi::activeWindow(HI::GUITestOpStatus &os, const GTGlobals::FindOptions &options) {
227     MainWindow *mw = AppContext::getMainWindow();
228     GT_CHECK_RESULT(mw != nullptr, "MainWindow == NULL", nullptr);
229 
230     QWidget *w = mw->getMDIManager()->getActiveWindow();
231     if (options.failIfNotFound) {
232         GT_CHECK_RESULT(w != nullptr, "Active window is not found", nullptr);
233     }
234     return w;
235 }
236 #undef GT_METHOD_NAME
237 
getActiveMdiWindowTitle()238 static QString getActiveMdiWindowTitle() {
239     MainWindow *mainWindow = AppContext::getMainWindow();
240     QWidget *mdiWindow = mainWindow == nullptr ? nullptr : mainWindow->getMDIManager()->getActiveWindow();
241     return mdiWindow == nullptr ? "<no active window>" : mdiWindow->windowTitle();
242 }
243 
244 #define GT_METHOD_NAME "getActiveObjectViewWindow"
getActiveObjectViewWindow(GUITestOpStatus & os,const QString & viewId)245 QWidget *GTUtilsMdi::getActiveObjectViewWindow(GUITestOpStatus &os, const QString &viewId) {
246     GObjectViewWindow *viewWindow = nullptr;
247     for (int time = 0; time < GT_OP_WAIT_MILLIS && viewWindow == nullptr; time += GT_OP_CHECK_MILLIS) {
248         GTGlobals::sleep(time > 0 ? GT_OP_CHECK_MILLIS : 0);
249         MainWindow *mainWindow = AppContext::getMainWindow();
250         QWidget *mdiWindow = mainWindow == nullptr ? nullptr : mainWindow->getMDIManager()->getActiveWindow();
251         if (mdiWindow == nullptr) {
252             continue;
253         }
254         GObjectViewWindow *activeViewWindow = qobject_cast<GObjectViewWindow *>(mdiWindow);
255         if (activeViewWindow != nullptr && activeViewWindow->getViewFactoryId() == viewId) {
256             viewWindow = activeViewWindow;
257         }
258     }
259     GT_CHECK_RESULT(viewWindow != nullptr, "View window is not found: " + viewId + ", active window: " + getActiveMdiWindowTitle(), nullptr);
260     return viewWindow;
261 }
262 #undef GT_METHOD_NAME
263 
264 #define GT_METHOD_NAME "checkNoObjectViewWindowIsOpened"
checkNoObjectViewWindowIsOpened(GUITestOpStatus & os,const QString & viewId)265 void GTUtilsMdi::checkNoObjectViewWindowIsOpened(GUITestOpStatus &os, const QString &viewId) {
266     QList<QWidget *> allWindows = getAllObjectViewWindows(viewId);
267     for (int time = 0; time < GT_OP_WAIT_MILLIS && !allWindows.isEmpty(); time += GT_OP_CHECK_MILLIS) {
268         GTGlobals::sleep(time > 0 ? GT_OP_CHECK_MILLIS : 0);
269         allWindows = getAllObjectViewWindows(viewId);
270     }
271     GT_CHECK(allWindows.isEmpty(), "Found object view windows: " + viewId + ", when expected no window to be present");
272     GTThread::waitForMainThread();
273 }
274 #undef GT_METHOD_NAME
275 
276 #define GT_METHOD_NAME "getAllObjectViewWindows"
getAllObjectViewWindows(const QString & viewId)277 QList<QWidget *> GTUtilsMdi::getAllObjectViewWindows(const QString &viewId) {
278     MainWindow *mainWindow = AppContext::getMainWindow();
279     QList<QWidget *> result;
280     if (mainWindow != nullptr) {
281         foreach (QWidget *window, mainWindow->getMDIManager()->getWindows()) {
282             GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(window);
283             if (objectViewWindow != nullptr && objectViewWindow->getViewFactoryId() == viewId) {
284                 result << objectViewWindow;
285             }
286         }
287     }
288     return result;
289 }
290 #undef GT_METHOD_NAME
291 
292 #define GT_METHOD_NAME "activeWindowTitle"
activeWindowTitle(HI::GUITestOpStatus & os)293 QString GTUtilsMdi::activeWindowTitle(HI::GUITestOpStatus &os) {
294     QWidget *w = activeWindow(os);
295     MWMDIWindow *mdi = qobject_cast<MWMDIWindow *>(w);
296     GT_CHECK_RESULT(mdi, "unexpected object type", QString());
297     return mdi->windowTitle();
298 }
299 #undef GT_METHOD_NAME
300 
301 #define GT_METHOD_NAME "activateWindow"
activateWindow(HI::GUITestOpStatus & os,const QString & windowTitlePart)302 void GTUtilsMdi::activateWindow(HI::GUITestOpStatus &os, const QString &windowTitlePart) {
303     MainWindow *mainWindow = AppContext::getMainWindow();
304 
305     GT_CHECK(mainWindow != nullptr, "MainWindow == nullptr");
306     CHECK(!activeWindowTitle(os).contains(windowTitlePart, Qt::CaseInsensitive), );
307 
308     GTGlobals::FindOptions options;
309     options.matchPolicy = Qt::MatchContains;
310     QWidget *window = findWindow(os, windowTitlePart, options);
311 
312     GTMenu::clickMainMenuItem(os, QStringList() << "Window" << window->windowTitle(), GTGlobals::UseMouse, Qt::MatchContains);
313     GTThread::waitForMainThread();
314 }
315 #undef GT_METHOD_NAME
316 
317 #define GT_METHOD_NAME "checkWindowIsActive"
checkWindowIsActive(HI::GUITestOpStatus & os,const QString & windowTitlePart)318 QWidget *GTUtilsMdi::checkWindowIsActive(HI::GUITestOpStatus &os, const QString &windowTitlePart) {
319     GT_CHECK_RESULT(!windowTitlePart.isEmpty(), "windowTitlePart is empty", nullptr);
320 
321     MainWindow *mainWindow = AppContext::getMainWindow();
322     GT_CHECK_RESULT(mainWindow != nullptr, "MainWindow == nullptr", nullptr);
323 
324     QWidget *window = nullptr;
325     for (int time = 0; time < GT_OP_WAIT_MILLIS && window == nullptr; time += GT_OP_CHECK_MILLIS) {
326         GTGlobals::sleep(time > 0 ? GT_OP_CHECK_MILLIS : 0);
327         QWidget *activeWindow = mainWindow->getMDIManager()->getActiveWindow();
328         if (activeWindow->windowTitle().contains(windowTitlePart, Qt::CaseInsensitive)) {
329             window = activeWindow;
330         }
331     }
332     GT_CHECK_RESULT(window != nullptr, "Window with title part '" + windowTitlePart + "' is not found", nullptr);
333     GTThread::waitForMainThread();
334     return window;
335 }
336 #undef GT_METHOD_NAME
337 
338 #define GT_METHOD_NAME "getMdiItemPosition"
getMdiItemPosition(HI::GUITestOpStatus & os,const QString & windowName)339 QPoint GTUtilsMdi::getMdiItemPosition(HI::GUITestOpStatus &os, const QString &windowName) {
340     QWidget *w = findWindow(os, windowName);
341     GT_CHECK_RESULT(w != nullptr, "MDI window not found", QPoint());
342     const QRect r = w->rect();
343     return w->mapToGlobal(r.center());
344 }
345 #undef GT_METHOD_NAME
346 
347 #define GT_METHOD_NAME "selectRandomRegion"
selectRandomRegion(HI::GUITestOpStatus & os,const QString & windowName)348 void GTUtilsMdi::selectRandomRegion(HI::GUITestOpStatus &os, const QString &windowName) {
349     QWidget *w = findWindow(os, windowName);
350     GT_CHECK(w != nullptr, "MDI window not found");
351     const QRect r = w->rect();
352     QPoint p = QPoint((r.topLeft().x() + r.bottomLeft().x()) / 2 + 5, r.center().y() / 2);
353     GTMouseDriver::moveTo(w->mapToGlobal(p));
354     GTMouseDriver::press();
355     GTMouseDriver::moveTo(w->mapToGlobal(r.center()));
356     GTMouseDriver::release();
357     GTThread::waitForMainThread();
358 }
359 #undef GT_METHOD_NAME
360 
361 namespace {
362 
isWidgetPartVisible(QWidget * widget)363 bool isWidgetPartVisible(QWidget *widget) {
364     CHECK(nullptr != widget, false);
365 
366     if (!widget->visibleRegion().isEmpty()) {
367         return true;
368     }
369 
370     foreach (QObject *child, widget->children()) {
371         if (child->isWidgetType() && isWidgetPartVisible(qobject_cast<QWidget *>(child))) {
372             return true;
373         }
374     }
375 
376     return false;
377 }
378 
379 }  // namespace
380 
381 #define GT_METHOD_NAME "isAnyPartOfWindowVisible"
isAnyPartOfWindowVisible(HI::GUITestOpStatus & os,const QString & windowName)382 bool GTUtilsMdi::isAnyPartOfWindowVisible(HI::GUITestOpStatus &os, const QString &windowName) {
383     GTGlobals::FindOptions options;
384     options.failIfNotFound = false;
385     QWidget *window = findWindow(os, windowName, options);
386     CHECK(nullptr != window, false);
387     return isWidgetPartVisible(window);
388 }
389 #undef GT_METHOD_NAME
390 
391 #define GT_METHOD_NAME "getTabBar"
getTabBar(HI::GUITestOpStatus & os)392 QTabBar *GTUtilsMdi::getTabBar(HI::GUITestOpStatus &os) {
393     MainWindow *mainWindow = AppContext::getMainWindow();
394     GT_CHECK_RESULT(mainWindow != nullptr, "MainWindow == nullptr", nullptr);
395 
396     QMdiArea *mdiArea = GTWidget::findExactWidget<QMdiArea *>(os, "MDI_Area", mainWindow->getQMainWindow());
397     GT_CHECK_RESULT(mdiArea != nullptr, "mdiArea == nullptr", nullptr);
398 
399     QTabBar *tabBar = mdiArea->findChild<QTabBar *>("", Qt::FindDirectChildrenOnly);
400     GT_CHECK_RESULT(tabBar != nullptr, "MDI tabbar not found", nullptr);
401 
402     return tabBar;
403 }
404 #undef GT_METHOD_NAME
405 
406 #define GT_METHOD_NAME "getTabBar"
getCurrentTab(HI::GUITestOpStatus & os)407 int GTUtilsMdi::getCurrentTab(HI::GUITestOpStatus &os) {
408     QTabBar *tabBar = getTabBar(os);
409     GT_CHECK_RESULT(tabBar != nullptr, "tabBar == NULL", -1);
410 
411     return tabBar->currentIndex();
412 }
413 #undef GT_METHOD_NAME
414 
415 #define GT_METHOD_NAME "clickTab"
clickTab(HI::GUITestOpStatus & os,int tabIndex)416 void GTUtilsMdi::clickTab(HI::GUITestOpStatus &os, int tabIndex) {
417     QTabBar *tabBar = getTabBar(os);
418     GT_CHECK_RESULT(tabBar != nullptr, "tabBar == NULL", );
419 
420     coreLog.info(QString("Try to click tab %1(%2)").arg(tabIndex).arg(tabBar->tabText(tabIndex)));
421     QPoint tabCenter = tabBar->mapToGlobal(tabBar->tabRect(tabIndex).center());
422     GTMouseDriver::moveTo(tabCenter);
423     GTMouseDriver::click();
424 }
425 #undef GT_METHOD_NAME
426 
427 #undef GT_CLASS_NAME
428 
429 }  // namespace U2
430