now, multi document editing works mostly as designed; unfortunately qt does not send an event when the visibility of a tab window changes

master
Marc Wäckerlin 6 years ago
parent 74910f249a
commit 9142588686
  1. 213
      src/scriptfile.hxx
  2. 142
      src/scriptfile.ui
  3. 444
      src/testgui.hxx
  4. 225
      src/testgui.ui
  5. 20
      src/webtester.cxx

@ -1,23 +1,32 @@
#ifndef __SCRIPTFILE__HXX #ifndef __SCRIPTFILE__HXX
#define __SCRIPTFILE__HXX #define __SCRIPTFILE__HXX
#include <commands.hxx>
#include <ui_scriptfile.hxx> #include <ui_scriptfile.hxx>
#include <QMessageBox>
#include <QScrollBar>
#include <QTextDocumentFragment>
#include <cassert> #include <cassert>
class ScriptFile: public QDockWidget, protected Ui::ScriptFile { class ScriptFile: public QDockWidget, protected Ui::ScriptFile {
Q_OBJECT Q_OBJECT
Q_SIGNALS: Q_SIGNALS:
void modified(ScriptFile*);
void link(QString); void link(QString);
void include(QString); void include(QString);
void close(ScriptFile*); void close(ScriptFile*);
void run(const QString&, const QString&, bool, Script&);
public: public:
ScriptFile(QWidget* p=0): QDockWidget(p) { ScriptFile(QWidget* p = nullptr): QDockWidget(p) {
setupUi(this); setupUi(this);
assert(connect(_editor, SIGNAL(textChanged()), SLOT(modified())));
assert(connect(_editor, SIGNAL(include(QString)), SIGNAL(include(QString)))); assert(connect(_editor, SIGNAL(include(QString)), SIGNAL(include(QString))));
assert(connect(_editor, SIGNAL(link(QString)), SIGNAL(link(QString)))); assert(connect(_editor, SIGNAL(link(QString)), SIGNAL(link(QString))));
_searchBar->hide(); _searchBar->hide();
_replaceBar->hide(); _replaceBar->hide();
_pageBar->hide(); _lineBar->hide();
_progress->hide();
_status->setCurrentIndex(STATUS_NONE);
} }
CodeEditor* editor() { CodeEditor* editor() {
return _editor; return _editor;
@ -30,10 +39,208 @@ class ScriptFile: public QDockWidget, protected Ui::ScriptFile {
setWindowTitle(name+"[*]"); setWindowTitle(name+"[*]");
setWindowModified(false); setWindowModified(false);
} }
public Q_SLOTS:
void load(QString name = QString()) {
if (isWindowModified() &&
QMessageBox::question(this, tr("Changes Not Saved"),
tr("Load script without saving changes?"))
!= QMessageBox::Yes)
return;
QString oldname(_name);
if (!name.isEmpty()) _name = name;
QFileInfo info(name);
if (info.absoluteDir()==QDir::current()) _name = info.fileName();
try {
QFile file(_name);
if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
throw std::runtime_error("file open failed");
_editor->setPlainText(QString::fromUtf8(file.readAll()));
if (file.error()!=QFileDevice::NoError)
throw std::runtime_error("file read failed");
setWindowTitle(_name+"[*]");
setWindowModified(false);
_status->setCurrentIndex(STATUS_NONE);
} catch(const std::exception& x) {
QMessageBox::critical(this, tr("Open Failed"),
tr("Reading test script failed, %2. "
"Cannot read test script from file %1.")
.arg(_name).arg(x.what()));
_name = oldname;
}
}
void save(QString name = QString()) {
QString oldname(_name);
if (!name.isEmpty()) _name = name;
QFile file(_name);
try {
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
throw std::runtime_error("file open failed");
QTextStream out(&file);
out<<_editor->toPlainText();
if (out.status()!=QTextStream::Ok)
throw std::runtime_error(std::string("file write failed (")
+char(out.status()+48)+")");
setWindowModified(false);
setWindowTitle(_name+"[*]");
} catch(const std::exception& x) {
QMessageBox::critical(this, tr("Save Failed"),
tr("Saving test script failed, %2. "
"Cannot write test script to file %1.")
.arg(_name).arg(x.what()));
_name = oldname;
}
}
void clear() {
if (isWindowModified() &&
QMessageBox::question(this, tr("Changes Not Saved"),
tr("Clear script without saving changes?"))
!= QMessageBox::Yes)
return;
_editor->clear();
setWindowModified(false);
}
void modified() {
setWindowModified(true);
modified(this);
}
void run() {
_progress->reset();
_progress->show();
_status->setCurrentIndex(STATUS_RUNNING);
bool oldRecordState(_record->isChecked());
_record->setChecked(false);
_record->setEnabled(false);
_run->setEnabled(false);
Script script;
try {
assert(connect(&script, SIGNAL(progress(QString, int, int)), SLOT(progress(QString, int, int))));
QString text(_editor->textCursor().selection().toPlainText());
if (text.isEmpty()) text = _editor->toPlainText();
run(_name, text, _screenshots->isChecked(), script);
_status->setCurrentIndex(STATUS_SUCCESS);
} catch (std::exception &x) {
_status->setCurrentIndex(STATUS_ERROR);
std::shared_ptr<Command> cmd(script.command());
if (cmd)
QMessageBox::critical(this,
tr("Test Failed"),
tr("<html>"
" <h1>Error [%1]</h1>"
" <dl>"
" <dt>Command:</dt><dd><code>%3</code></dd>"
" <dt>File:</dt><dd>%4</dd>"
" <dt>Line:</dt><dd>%5</dd>"
" <dt>Error Message:</dt><dd><pre>%2</pre></dd>"
" </dl>"
"</html>")
.arg(demangle(typeid(x).name()))
.arg(x.what())
.arg(cmd->command())
.arg(cmd->file())
.arg(cmd->line()));
else
QMessageBox::critical(this,
tr("Test Failed"),
tr("<html>"
" <h1>Error [%1]</h1>"
" <p><code>%2</code></p>"
"</html>")
.arg(demangle(typeid(x).name()))
.arg(QString(x.what()).replace("\n", "<br/>")));
}
_run->setEnabled(true);
_record->setEnabled(true);
_record->setChecked(oldRecordState);
_progress->hide();
}
void appendCommand(const QString& txt) {
if (!_record->isChecked()) return;
_editor->appendPlainText(txt);
QScrollBar *vb(_editor->verticalScrollBar());
_editor->moveCursor(QTextCursor::End);
_editor->ensureCursorVisible();
if (!vb) return;
vb->setValue(vb->maximum());
}
void appendCommand(const QString& selector, const QString& txt) {
if (!_record->isChecked()) return;
QString text(_editor->toPlainText());
QStringList lines(text.split("\n"));
bool changed(false);
while (lines.size() &&
(lines.last()=="click "+selector ||
lines.last().startsWith("setvalue "+selector+" -> "))) {
lines.removeLast();
changed = true;
}
if (changed) {
_editor->setPlainText(lines.join("\n"));
_editor->moveCursor(QTextCursor::End);
_editor->ensureCursorVisible();
}
appendCommand(txt);
}
void appendWebLoadFinished(bool ok) {
if (!_record->isChecked()) return;
QString text(_editor->toPlainText());
QStringList lines(text.split("\n"));
if (ok && lines.size()>1 &&
lines.last().startsWith("expect urlChanged") &&
lines.at(lines.size()-2)=="expect loadStarted") {
// replace three expect lines by one single line
QString url(lines.last().replace("expect urlChanged", "").trimmed());
lines.removeLast(); lines.removeLast();
_editor->setPlainText(lines.join("\n"));
_editor->moveCursor(QTextCursor::End);
_editor->ensureCursorVisible();
appendCommand("expect load "+url);
} else {
appendCommand("expect loadFinished "+QString(ok?"true":"false"));
}
}
void unsupportedContent(QNetworkReply* reply) {
if (!_record->isChecked()) return;
QString filename(reply->url().toString().split('/').last());
if (reply->header(QNetworkRequest::ContentDispositionHeader).isValid()) {
QString part(reply->header(QNetworkRequest::ContentDispositionHeader)
.toString());
if (part.contains(QRegularExpression("attachment; *filename="))) {
part.replace(QRegularExpression(".*attachment; *filename="), "");
if (part.size()) filename = part;
}
}
QString text(_editor->toPlainText());
int pos1(text.lastIndexOf(QRegularExpression("^do ")));
int pos2(text.lastIndexOf(QRegularExpression("^load ")));
int pos3(text.lastIndexOf(QRegularExpression("^click ")));
text.insert(std::max({pos1, pos2, pos3}), "download "+filename);
_editor->setPlainText(text);
_editor->moveCursor(QTextCursor::End);
_editor->ensureCursorVisible();
}
void progress(const QString& txt, int pos, int max) {
_progress->setFormat(QString("%1 — %p%").arg(txt));
_progress->setMinimum(0);
_progress->setMaximum(max);
_progress->setValue(pos);
}
void runEnabled(bool f = true) {
_run->setEnabled(false);
}
void on__run_clicked() {
run();
}
protected: protected:
void closeEvent (QCloseEvent*) { void closeEvent(QCloseEvent*) {
close(this); close(this);
} }
private:
enum RunStatus {
STATUS_NONE = 0,
STATUS_RUNNING,
STATUS_SUCCESS,
STATUS_ERROR
};
private: private:
QString _name; QString _name;
}; };

@ -7,16 +7,135 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>628</width> <width>628</width>
<height>378</height> <height>593</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>DockW&amp;idget</string> <string>Do&amp;ckWidget</string>
</property> </property>
<widget class="QWidget" name="dockWidgetContents"> <widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="CodeEditor" name="_editor"/> <layout class="QVBoxLayout" name="verticalLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="CodeEditor" name="_editor"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QPushButton" name="_record">
<property name="text">
<string>Record</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="_run">
<property name="text">
<string>Run</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="_screenshots">
<property name="text">
<string>Screenshots</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QStackedWidget" name="_status">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_6"/>
<widget class="QWidget" name="page_5">
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;div style=&quot;font-size: xx-large&quot;&gt;⌛&lt;/div&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;div style=&quot;font-size: xx-large; color: green&quot;&gt;✔&lt;/div&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;div style=&quot;font-size: xx-large; color: red&quot;&gt;✘&lt;/div&gt;</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QWidget" name="_searchBar" native="true"> <widget class="QWidget" name="_searchBar" native="true">
@ -72,21 +191,28 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="_pageBar" native="true"> <widget class="QWidget" name="_lineBar" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<widget class="QSpinBox" name="_page"/> <widget class="QSpinBox" name="_line"/>
</item> </item>
<item> <item>
<widget class="QPushButton" name="_goPage"> <widget class="QPushButton" name="_goLine">
<property name="text"> <property name="text">
<string>page</string> <string>line</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QProgressBar" name="_progress">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>

@ -1,7 +1,7 @@
/*! @file /*! @file
@id $Id$ @id $Id$
*/ */
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
#ifndef TESTGUI_HXX #ifndef TESTGUI_HXX
@ -24,7 +24,6 @@
#include <stdexcept> #include <stdexcept>
#include <QNetworkReply> #include <QNetworkReply>
#include <QEvent> #include <QEvent>
#include <QTextDocumentFragment>
#include <mrw/stdext.hxx> #include <mrw/stdext.hxx>
class TestGUI: public QMainWindow, protected Ui::TestGUI { class TestGUI: public QMainWindow, protected Ui::TestGUI {
@ -39,7 +38,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
_inEventFilter(false) { _inEventFilter(false) {
setWindowTitle("[*]"); setWindowTitle("[*]");
setupUi(this); setupUi(this);
menuViews->addAction(_scriptDock->toggleViewAction()); setDockOptions(dockOptions()|QMainWindow::GroupedDragging);
menuViews->addAction(_setupScriptDock->toggleViewAction()); menuViews->addAction(_setupScriptDock->toggleViewAction());
menuViews->addAction(_scriptCommandsDock->toggleViewAction()); menuViews->addAction(_scriptCommandsDock->toggleViewAction());
menuViews->addAction(_domDock->toggleViewAction()); menuViews->addAction(_domDock->toggleViewAction());
@ -48,8 +47,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
menuViews->addAction(_logDock->toggleViewAction()); menuViews->addAction(_logDock->toggleViewAction());
menuViews->addAction(_sourceDock->toggleViewAction()); menuViews->addAction(_sourceDock->toggleViewAction());
menuViews->addAction(_executeDock->toggleViewAction()); menuViews->addAction(_executeDock->toggleViewAction());
_progress->hide();
_status->setCurrentIndex(STATUS_NONE);
QSettings settings("mrw", "webtester"); QSettings settings("mrw", "webtester");
restoreGeometry(settings.value("geometry").toByteArray()); restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowstate").toByteArray()); restoreState(settings.value("windowstate").toByteArray());
@ -64,22 +62,22 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
_web->installEventFilter(this); // track mouse and keyboard _web->installEventFilter(this); // track mouse and keyboard
pg->setForwardUnsupportedContent(true); pg->setForwardUnsupportedContent(true);
_commands->setText(Script().commands(Script::HTML)); _commands->setText(Script().commands(Script::HTML));
assert(connect(menuFile, SIGNAL(aboutToShow()), SLOT(fileMenuOpened())));
assert(connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)),
SLOT(focusChanged(QWidget*, QWidget*))));
assert(connect(pg, SIGNAL(uploadFile(QString)), SLOT(uploadFile(QString)))); assert(connect(pg, SIGNAL(uploadFile(QString)), SLOT(uploadFile(QString))));
assert(connect(pg, SIGNAL(unsupportedContent(QNetworkReply*)), assert(connect(pg, SIGNAL(unsupportedContent(QNetworkReply*)),
SLOT(unsupportedContent(QNetworkReply*)))); SLOT(unsupportedContent(QNetworkReply*))));
assert(connect(pg, SIGNAL(downloadRequested(const QNetworkRequest&)), assert(connect(pg, SIGNAL(downloadRequested(const QNetworkRequest&)),
SLOT(downloadRequested(const QNetworkRequest&)))); SLOT(downloadRequested(const QNetworkRequest&))));
//assert(connect(_testscript, SIGNAL(include(QString)), SLOT(include(QString))));
assert(connect(_testscript, SIGNAL(link(QString)), SLOT(include(QString))));
if (setupScript.size()) loadSetup(setupScript); if (setupScript.size()) loadSetup(setupScript);
if (scriptFile.size()) loadFile(scriptFile); if (scriptFile.size()) load(scriptFile);
} }
virtual ~TestGUI() {} virtual ~TestGUI() {}
public Q_SLOTS: public Q_SLOTS:
void on__load_clicked() { void on__load_clicked() {
enterText(true); enterText(true);
if (_record->isChecked()) appendCommand("load "+map(_url->currentText()));
appendCommand("load "+map(_url->currentText()));
storeUrl(_url->currentText()); storeUrl(_url->currentText());
_webprogress->setFormat(_url->currentText()); _webprogress->setFormat(_url->currentText());
_web->load(_url->currentText()); _web->load(_url->currentText());
@ -91,123 +89,34 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
void on__actionOpen_triggered() { void on__actionOpen_triggered() {
QString name(QFileDialog::getOpenFileName(this, tr("Open Test Script"))); QString name(QFileDialog::getOpenFileName(this, tr("Open Test Script")));
if (name.isEmpty()) return; if (name.isEmpty()) return;
loadFile(name); load(name);
_status->setCurrentIndex(STATUS_NONE);
} }
void on__actionOpenSetupScript_triggered() { void on__actionOpenSetupScript_triggered() {
QString name(QFileDialog::getOpenFileName(this, tr("Open Setup Script"))); QString name(QFileDialog::getOpenFileName(this, tr("Open Setup Script")));
if (name.isEmpty()) return; if (name.isEmpty()) return;
loadSetup(name); loadSetup(name);
_status->setCurrentIndex(STATUS_NONE);
} }
void on__actionRevertToSaved_triggered() { void on__actionRevertToSaved_triggered() {
loadFile(_filename); ScriptFile* active(activeScriptFile());
_status->setCurrentIndex(STATUS_NONE); if (active) active->load();
} }
void on__actionSaveAs_triggered() { void on__actionSaveAs_triggered() {
ScriptFile* active(activeScriptFile());
if (!active) return;
QString name(QFileDialog::getSaveFileName(this, tr("Save Test Script"))); QString name(QFileDialog::getSaveFileName(this, tr("Save Test Script")));
if (name.isEmpty()) return; if (!name.isEmpty()) active->save(name);
_filename = name;
on__actionSave_triggered();
_status->setCurrentIndex(STATUS_NONE);
} }
void on__actionSave_triggered() { void on__actionSave_triggered() {
QFile file(_filename); ScriptFile* active(activeScriptFile());
try { if (active) active->save();
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
throw std::runtime_error("file open failed");
QTextStream out(&file);
out<<_testscript->toPlainText();
if (out.status()!=QTextStream::Ok)
throw std::runtime_error(std::string("file write failed (")
+char(out.status()+48)+")");
_actionSave->setEnabled(true);
_actionRevertToSaved->setEnabled(true);
setWindowModified(false);
setWindowTitle(_filename+"[*]");
} catch(const std::exception& x) {
QMessageBox::critical(this, tr("Save Failed"),
tr("Saving test script failed, %2. "
"Cannot write test script to file %1.")
.arg(_filename).arg(x.what()));
}
_status->setCurrentIndex(STATUS_NONE);
} }
void on__actionClear_triggered() { void on__actionClear_triggered() {
if (isWindowModified() && ScriptFile* active(activeScriptFile());
QMessageBox::question(this, tr("Changes Not Saved"), if (active) active->clear();
tr("Clear script without saving changes?")) }
!= QMessageBox::Yes) void on__actionRun_triggered() {
return; ScriptFile* active(activeScriptFile());
_testscript->clear(); if (active) active->run();
_log->clear();
_filename.clear();
_actionSave->setEnabled(false);
_actionRevertToSaved->setEnabled(false);
setWindowTitle("[*]");
setWindowModified(false);
_status->setCurrentIndex(STATUS_NONE);
}
void on__run_clicked() {
_progress->reset();
_progress->show();
_status->setCurrentIndex(STATUS_RUNNING);
bool oldRecordState(_record->isChecked());
_record->setChecked(false);
_record->setEnabled(false);
_run->setEnabled(false);
Script script;
try {
connect(&script, SIGNAL(logging(QString)), SLOT(logging(QString)));
connect(&script, SIGNAL(progress(QString, int, int)), SLOT(progress(QString, int, int)));
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuite"));
if (_setupscriptactive->isEnabled()
&& _setupscriptactive->isChecked()) {
script.parse(_setupscript->toPlainText().split('\n'), "setup");
script.run(_web->page()->mainFrame(), testsuites, QString(),
_screenshots->isChecked());
script.reset();
}
QString text(_testscript->textCursor().selection().toPlainText());
if (text.isEmpty()) text = _testscript->toPlainText();
script.parse(text.split('\n'), "script");
script.run(_web->page()->mainFrame(), testsuites, QString(),
_screenshots->isChecked());
_status->setCurrentIndex(STATUS_SUCCESS);
} catch (std::exception &x) {
_status->setCurrentIndex(STATUS_ERROR);
std::shared_ptr<Command> cmd(script.command());
if (cmd)
QMessageBox::critical(this,
tr("Test Failed"),
tr("<html>"
" <h1>Error [%1]</h1>"
" <dl>"
" <dt>Command:</dt><dd><code>%3</code></dd>"
" <dt>File:</dt><dd>%4</dd>"
" <dt>Line:</dt><dd>%5</dd>"
" <dt>Error Message:</dt><dd><pre>%2</pre></dd>"
" </dl>"
"</html>")
.arg(demangle(typeid(x).name()))
.arg(x.what())
.arg(cmd->command())
.arg(cmd->file())
.arg(cmd->line()));
else
QMessageBox::critical(this,
tr("Test Failed"),
tr("<html>"
" <h1>Error [%1]</h1>"
" <p><code>%2</code></p>"
"</html>")
.arg(demangle(typeid(x).name()))
.arg(QString(x.what()).replace("\n", "<br/>")));
}
_run->setEnabled(true);
_record->setEnabled(true);
_record->setChecked(oldRecordState);
_progress->hide();
} }
void on__focused_clicked() { void on__focused_clicked() {
enterText(true); enterText(true);
@ -228,8 +137,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
} }
void on__web_linkClicked(const QUrl& url) { void on__web_linkClicked(const QUrl& url) {
enterText(true); enterText(true);
if (_record->isChecked()) appendCommand("load "+map(url.url()));
appendCommand("load "+map(url.url()));
} }
void on__web_loadProgress(int progress) { void on__web_loadProgress(int progress) {
enterText(true); enterText(true);
@ -237,8 +145,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
} }
void on__web_loadStarted() { void on__web_loadStarted() {
enterText(true); enterText(true);
if (_record->isChecked()) appendCommand("expect "+map("loadStarted"));
appendCommand("expect "+map("loadStarted"));
_webprogress->setValue(0); _webprogress->setValue(0);
_urlStack->setCurrentIndex(PROGRESS_VIEW); _urlStack->setCurrentIndex(PROGRESS_VIEW);
} }
@ -252,8 +159,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
_webprogress->setFormat(url.url()); _webprogress->setFormat(url.url());
storeUrl(url); storeUrl(url);
enterText(true); enterText(true);
if (_record->isChecked()) appendCommand("expect "+map("urlChanged "+url.url()));
appendCommand("expect "+map("urlChanged "+url.url()));
} }
void on__web_selectionChanged() { void on__web_selectionChanged() {
_source->setPlainText(_web->hasSelection() _source->setPlainText(_web->hasSelection()
@ -262,24 +168,8 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
} }
void on__web_loadFinished(bool ok) { void on__web_loadFinished(bool ok) {
enterText(true); enterText(true);
if (_record->isChecked()) { for (auto testscript: _testscripts)
QString text(_testscript->toPlainText()); testscript->appendWebLoadFinished(ok);
QStringList lines(text.split("\n"));
if (ok && lines.size()>1 &&
lines.last().startsWith("expect urlChanged") &&
lines.at(lines.size()-2)=="expect loadStarted") {
// replace three expect lines by one single line
QString url(lines.last().replace("expect urlChanged", "").trimmed());
lines.removeLast(); lines.removeLast();
_testscript->setPlainText(lines.join("\n"));
_testscript->moveCursor(QTextCursor::End);
_testscript->ensureCursorVisible();
appendCommand("expect "+map("load "+url));
} else {
appendCommand("expect "+map("loadFinished "
+QString(ok?"true":"false")));
}
}
_urlStack->setCurrentIndex(URL_VIEW); _urlStack->setCurrentIndex(URL_VIEW);
on__web_selectionChanged(); on__web_selectionChanged();
setLinks(); setLinks();
@ -287,8 +177,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
setDom(); setDom();
} }
void on__setupscript_textChanged() { void on__setupscript_textChanged() {
bool oldRecordState(_record->isChecked()); for (auto testscript: _testscripts) testscript->runEnabled(false);
_run->setEnabled(false);
_setupscriptactive->setEnabled(false); _setupscriptactive->setEnabled(false);
try { try {
_setupscriptstatus->setText(trUtf8("?")); _setupscriptstatus->setText(trUtf8("?"));
@ -305,8 +194,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
} catch (std::exception &x) { } catch (std::exception &x) {
_setupscriptstatus->setText(trUtf8("")); _setupscriptstatus->setText(trUtf8(""));
} }
_run->setEnabled(true); for (auto testscript: _testscripts) testscript->runEnabled(true);
_record->setChecked(oldRecordState);
} }
void on__forms_currentItemChanged(QTreeWidgetItem* item, QTreeWidgetItem*) { void on__forms_currentItemChanged(QTreeWidgetItem* item, QTreeWidgetItem*) {
if (!item) return; if (!item) return;
@ -318,32 +206,13 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
} }
void uploadFile(QString filename) { void uploadFile(QString filename) {
enterText(true); enterText(true);
if (_record->isChecked()) appendCommand("upload "+map(filename));
appendCommand("upload "+map(filename));
} }
void unsupportedContent(QNetworkReply* reply) { void unsupportedContent(QNetworkReply* reply) {
if (!_record->isChecked()) return; for (auto testscript: _testscripts) testscript->unsupportedContent(reply);
QString filename(reply->url().toString().split('/').last());
if (reply->header(QNetworkRequest::ContentDispositionHeader).isValid()) {
QString part(reply->header(QNetworkRequest::ContentDispositionHeader)
.toString());
if (part.contains(QRegularExpression("attachment; *filename="))) {
part.replace(QRegularExpression(".*attachment; *filename="), "");
if (part.size()) filename = part;
}
}
QString text(_testscript->toPlainText());
int pos1(text.lastIndexOf(QRegularExpression("^do ")));
int pos2(text.lastIndexOf(QRegularExpression("^load ")));
int pos3(text.lastIndexOf(QRegularExpression("^click ")));
text.insert(std::max({pos1, pos2, pos3}), "download "+filename);
_testscript->setPlainText(text);
_testscript->moveCursor(QTextCursor::End);
_testscript->ensureCursorVisible();
} }
void downloadRequested(const QNetworkRequest&) { void downloadRequested(const QNetworkRequest&) {
if (_record->isChecked()) appendCommand("download2");
appendCommand("download2");
} }
void logging(const QString& txt) { void logging(const QString& txt) {
_log->appendPlainText(txt); _log->appendPlainText(txt);
@ -351,49 +220,80 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
if (!vb) return; if (!vb) return;
vb->setValue(vb->maximum()); vb->setValue(vb->maximum());
} }
void progress(const QString& txt, int pos, int max) { void fileMenuOpened() {
_progress->setFormat(QString("%1 — %p%").arg(txt)); focusChanged(nullptr, nullptr);
_progress->setMinimum(0);
_progress->setMaximum(max);
_progress->setValue(pos);
} }
void appendCommand(const QString& txt) { void modified(ScriptFile* win) {
_testscript->appendPlainText(txt); focusChanged(nullptr, win);
QScrollBar *vb(_testscript->verticalScrollBar());
_testscript->moveCursor(QTextCursor::End);
_testscript->ensureCursorVisible();
if (!vb) return;
vb->setValue(vb->maximum());
} }
void include(QString name) { void focusChanged(QWidget*, QWidget* focus) {
if (_testscripts.contains(name)) return; ScriptFile* active(activeScriptFile(focus));
if (active)
setWindowFilePath(active->name());
else
setWindowFilePath(QString());
_actionRevertToSaved->setEnabled(active);
_actionSaveAs->setEnabled(active);
_actionSave->setEnabled(active&&active->isWindowModified());
_actionClear->setEnabled(active);
_actionRun->setEnabled(active);
}
void activate(QString name) {
QFileInfo info(name);
if (info.absoluteDir()==QDir::current()) name = info.fileName();
if (!_testscripts.contains(name)) return load(name);
_testscripts[name]->show();
_testscripts[name]->raise();
_testscripts[name]->activateWindow();
}
void load(QString name) {
QFileInfo info(name);
if (info.absoluteDir()==QDir::current()) name = info.fileName();
if (_testscripts.contains(name)) try {
_testscripts[name]->load(name);
return activate(name);
} catch(const std::exception& x) {
remove(_testscripts[name]);
}
QDockWidget* first(_testscripts.isEmpty()?_setupScriptDock:_testscripts.last());
_testscripts[name] = new ScriptFile(this); _testscripts[name] = new ScriptFile(this);
// assert(connect(_testscripts[name], SIGNAL(include(QString)), SLOT(include(QString)))); assert(connect(_testscripts[name], SIGNAL(modified(ScriptFile*)), SLOT(modified(ScriptFile*))));
assert(connect(_testscripts[name], SIGNAL(link(QString)), SLOT(include(QString)))); assert(connect(_testscripts[name], SIGNAL(link(QString)), SLOT(activate(QString))));
assert(connect(_testscripts[name], SIGNAL(close(ScriptFile*)), SLOT(remove(ScriptFile*)))); assert(connect(_testscripts[name], SIGNAL(close(ScriptFile*)), SLOT(remove(ScriptFile*))));
QFile file(name); assert(connect(_testscripts[name], SIGNAL(run(const QString&, const QString&, bool, Script&)), SLOT(run(const QString&, const QString&, bool, Script&))));
try { try {
if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) _testscripts[name]->load(name);
throw std::runtime_error("file open failed"); tabifyDockWidget(first, _testscripts[name]);
_testscripts[name]->editor()->setPlainText(QString::fromUtf8(file.readAll())); activate(name);
if (file.error()!=QFileDevice::NoError)
throw std::runtime_error("file read failed");
_testscripts[name]->name(name);
tabifyDockWidget(_scriptDock, _testscripts[name]);
// QDockWidget* d(0);
// for (QWidget* w(QApplication::focusWidget()); w&&!(d=qobject_cast<QDockWidget*>(w));
// w=qobject_cast<QWidget*>(w->parent()));
// if (d) d->raise();
_testscripts[name]->raise();
} catch(const std::exception& x) { } catch(const std::exception& x) {
remove(_testscripts[name]); remove(_testscripts[name]);
} }
} }
void remove(ScriptFile* scriptfile) { void remove(ScriptFile* scriptfile) {
/// @todo check if modified
_testscripts.remove(scriptfile->name()); _testscripts.remove(scriptfile->name());
delete scriptfile; delete scriptfile;
} }
void run(const QString& name, const QString& text, bool screenshots, Script& script) {
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuite"));
assert(connect(&script, SIGNAL(logging(QString)), SLOT(logging(QString))));
if (_setupscriptactive->isEnabled()
&& _setupscriptactive->isChecked()) {
script.parse(_setupscript->toPlainText().split('\n'), "setup");
script.run(_web->page()->mainFrame(), testsuites, QString(), screenshots);
script.reset();
}
script.parse(text.split('\n'), name);
script.run(_web->page()->mainFrame(), testsuites, QString(), screenshots);
}
protected: protected:
ScriptFile* activeScriptFile(QWidget* focus=nullptr) {
//for (auto win: _testscripts) if (win->isActiveWindow()) return win;
ScriptFile* active(nullptr);
for (QObject* wid(focus?focus:QApplication::focusWidget()); !active && wid; wid = wid->parent())
active = dynamic_cast<ScriptFile*>(wid);
return active;
}
void closeEvent(QCloseEvent* event) { void closeEvent(QCloseEvent* event) {
QSettings settings("mrw", "webtester"); QSettings settings("mrw", "webtester");
settings.setValue("geometry", saveGeometry()); settings.setValue("geometry", saveGeometry());
@ -440,41 +340,39 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
case QEvent::MouseButtonRelease: { case QEvent::MouseButtonRelease: {
enterText(true); enterText(true);
_lastFocused=element; _lastFocused=element;
if (_record->isChecked()) { if (!element.isNull()) {
if (!element.isNull()) { QString selected(selector(element));
QString selected(selector(element)); if (handleMooTools(_lastFocused)) {
if (handleMooTools(_lastFocused)) { // handled in handleMooTools
// handled in handleMooTools } else if (_lastFocused.tagName()=="SELECT") {
} else if (_lastFocused.tagName()=="SELECT") { // click on a select results in a value change
// click on a select results in a value change // find all selected options ...
// find all selected options ... QStringList v;
QStringList v; for (QWebElement option: _lastFocused.findAll("option")) {
for (QWebElement option: _lastFocused.findAll("option")) { //! @bug QT does not support selected
//! @bug QT does not support selected if (option.evaluateJavaScript("this.selected").toBool())
if (option.evaluateJavaScript("this.selected").toBool()) v += value(option);
v += value(option);
}
setValue(selected, v);
} else if (_lastFocused.tagName()=="TEXTAREA" ||
(_lastFocused.tagName()=="INPUT" &&
_lastFocused.attribute("type")=="text")) {
// user clickt in a text edit field, so not the klick
// is important, but the text that will be typed
_typing = true;
} else {
if (_web->page()->selectedText() != "") {
// user has selected a text, append a check
appendCommand("exists "+map(selected)
+" -> "+_web->page()->selectedText());
_web->page()->findText(QString());
} else {
// user has clicked without selection, append a click
appendCommand("click "+map(selected));
}
} }
setValue(selected, v);
} else if (_lastFocused.tagName()=="TEXTAREA" ||
(_lastFocused.tagName()=="INPUT" &&
_lastFocused.attribute("type")=="text")) {
// user clickt in a text edit field, so not the klick
// is important, but the text that will be typed
_typing = true;
} else { } else {
appendCommand("# click, but where?"); if (_web->page()->selectedText() != "") {
// user has selected a text, append a check
appendCommand("exists "+map(selected)
+" -> "+_web->page()->selectedText());
_web->page()->findText(QString());
} else {
// user has clicked without selection, append a click
appendCommand("click "+map(selected));
}
} }
} else {
appendCommand("# click, but where?");
} }
} break; } break;
case QEvent::MouseButtonPress: { case QEvent::MouseButtonPress: {
@ -505,26 +403,6 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
_url->setCurrentText(u.url()); _url->setCurrentText(u.url());
} }
} }
void loadFile(QString name) {
QFile file(name);
try {
if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
throw std::runtime_error("file open failed");
_testscript->setPlainText(QString::fromUtf8(file.readAll()));
if (file.error()!=QFileDevice::NoError)
throw std::runtime_error("file read failed");
_filename = name;
_actionSave->setEnabled(true);
_actionRevertToSaved->setEnabled(true);
setWindowTitle(name+"[*]");
setWindowModified(false);
} catch(const std::exception& x) {
QMessageBox::critical(this, tr("Open Failed"),
tr("Reading test script failed, %2. "
"Cannot read test script from file %1.")
.arg(name).arg(x.what()));
}
}
void loadSetup(QString name) { void loadSetup(QString name) {
QFile file(name); QFile file(name);
try { try {
@ -535,10 +413,10 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
throw std::runtime_error("file read failed"); throw std::runtime_error("file read failed");
on__setupscript_textChanged(); on__setupscript_textChanged();
} catch(const std::exception& x) { } catch(const std::exception& x) {
QMessageBox::critical(this, tr("Open Failed"), QMessageBox::critical(this, tr("Open Failed"),
tr("Reading test script failed, %2. " tr("Reading test script failed, %2. "
"Cannot read test script from file %1.") "Cannot read test script from file %1.")
.arg(name).arg(x.what())); .arg(name).arg(x.what()));
} }
} }
void enterText(bool force=false) { void enterText(bool force=false) {
@ -561,9 +439,9 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
return QString(); // error return QString(); // error
} }
void highlight(QWebElement element) { void highlight(QWebElement element) {
element element
.evaluateJavaScript("var selection = window.getSelection();" .evaluateJavaScript("var selection = window.getSelection();"
"selection.setBaseAndExtent(this, 0, this, 1);"); "selection.setBaseAndExtent(this, 0, this, 1);");
} }
QWebElement focused(QMouseEvent* event = 0) { QWebElement focused(QMouseEvent* event = 0) {
for (QWebElement element: _web->page()->currentFrame()->findAllElements("*")) { for (QWebElement element: _web->page()->currentFrame()->findAllElements("*")) {
@ -644,41 +522,26 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
return in; return in;
} }
void javascript(const QString& selector, QString code) { void javascript(const QString& selector, QString code) {
if (_record->isChecked()) appendCommand("do "+map(selector)+"\n "
appendCommand("do "+map(selector)+"\n " +map(code).replace("\n", "\n "));
+map(code).replace("\n", "\n "));
}
void cleanup(const QString& selector) {
QString text(_testscript->toPlainText());
QStringList lines(text.split("\n"));
bool changed(false);
while (lines.size() &&
(lines.last()=="click "+selector ||
lines.last().startsWith("setvalue "+selector+" -> "))) {
lines.removeLast();
changed = true;
}
if (changed) {
_testscript->setPlainText(lines.join("\n"));
_testscript->moveCursor(QTextCursor::End);
_testscript->ensureCursorVisible();
}
} }
void setValue(const QString& selector, QString code) { void setValue(const QString& selector, QString code) {
if (_record->isChecked()) { appendCommand(selector,
cleanup(selector); "setvalue "+map(selector)+" -> '"
appendCommand("setvalue "+map(selector)+" -> '" +map(code).replace("'", "\\'").replace("\n", "\\n")+"'");
+map(code).replace("'", "\\'").replace("\n", "\\n")+"'");
}
} }
void setValue(const QString& selector, QStringList code) { void setValue(const QString& selector, QStringList code) {
if (_record->isChecked()) { appendCommand(selector,
cleanup(selector); "setvalue "+map(selector)+" -> '"+
appendCommand("setvalue "+map(selector)+" -> '"+ map(code.replaceInStrings("'", "\\'")
map(code.replaceInStrings("'", "\\'") .replaceInStrings("\n", "\\n")
.replaceInStrings("\n", "\\n") .join("', '")+"'"));
.join("', '")+"'")); }
} void appendCommand(const QString& txt) {
for (auto testscript: _testscripts) testscript->appendCommand(txt);
}
void appendCommand(const QString& selector, const QString& txt) {
for (auto testscript: _testscripts) testscript->appendCommand(selector, txt);
} }
bool handleMooTools(QWebElement element) { bool handleMooTools(QWebElement element) {
QString selected(selector(element)); QString selected(selector(element));
@ -697,10 +560,9 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
return true; return true;
} else if (mooComboItem.hasMatch()) { } else if (mooComboItem.hasMatch()) {
// special treatment for item in moo tools combobox // special treatment for item in moo tools combobox
appendCommand appendCommand("click realmouse "+map("li.active-result[data-option-array-index=\""
("click realmouse "+map("li.active-result[data-option-array-index=\"" +element.attribute("data-option-array-index")
+element.attribute("data-option-array-index") +"\"]"));
+"\"]"));
appendCommand("sleep "+map("1")); appendCommand("sleep "+map("1"));
return true; return true;
} else if (element.tagName()=="INPUT") { } else if (element.tagName()=="INPUT") {
@ -861,7 +723,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
case QEvent::LayoutDirectionChange: return "QEvent::LayoutDirectionChange - The direction of layouts changed."; case QEvent::LayoutDirectionChange: return "QEvent::LayoutDirectionChange - The direction of layouts changed.";
case QEvent::LayoutRequest: return "QEvent::LayoutRequest - Widget layout needs to be redone."; case QEvent::LayoutRequest: return "QEvent::LayoutRequest - Widget layout needs to be redone.";
case QEvent::Leave: return "QEvent::Leave - Mouse leaves widget's boundaries."; case QEvent::Leave: return "QEvent::Leave - Mouse leaves widget's boundaries.";
//case QEvent::LeaveEditFocus: return "QEvent::LeaveEditFocus - An editor widget loses focus for editing. QT_KEYPAD_NAVIGATION must be defined."; //case QEvent::LeaveEditFocus: return "QEvent::LeaveEditFocus - An editor widget loses focus for editing. QT_KEYPAD_NAVIGATION must be defined.";
case QEvent::LeaveWhatsThisMode: return "QEvent::LeaveWhatsThisMode - Send to toplevel widgets when the application leaves \"What's This?\" mode."; case QEvent::LeaveWhatsThisMode: return "QEvent::LeaveWhatsThisMode - Send to toplevel widgets when the application leaves \"What's This?\" mode.";
case QEvent::LocaleChange: return "QEvent::LocaleChange - The system locale has changed."; case QEvent::LocaleChange: return "QEvent::LocaleChange - The system locale has changed.";
case QEvent::NonClientAreaMouseButtonDblClick: return "QEvent::NonClientAreaMouseButtonDblClick - A mouse double click occurred outside the client area."; case QEvent::NonClientAreaMouseButtonDblClick: return "QEvent::NonClientAreaMouseButtonDblClick - A mouse double click occurred outside the client area.";
@ -887,7 +749,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
case QEvent::Polish: return "QEvent::Polish - The widget is polished."; case QEvent::Polish: return "QEvent::Polish - The widget is polished.";
case QEvent::PolishRequest: return "QEvent::PolishRequest - The widget should be polished."; case QEvent::PolishRequest: return "QEvent::PolishRequest - The widget should be polished.";
case QEvent::QueryWhatsThis: return "QEvent::QueryWhatsThis - The widget should accept the event if it has \"What's This?\" help."; case QEvent::QueryWhatsThis: return "QEvent::QueryWhatsThis - The widget should accept the event if it has \"What's This?\" help.";
//case QEvent::ReadOnlyChange: return "QEvent::ReadOnlyChange - Widget's read-only state has changed (since Qt 5.4)."; //case QEvent::ReadOnlyChange: return "QEvent::ReadOnlyChange - Widget's read-only state has changed (since Qt 5.4).";
case QEvent::RequestSoftwareInputPanel: return "QEvent::RequestSoftwareInputPanel - A widget wants to open a software input panel (SIP)."; case QEvent::RequestSoftwareInputPanel: return "QEvent::RequestSoftwareInputPanel - A widget wants to open a software input panel (SIP).";
case QEvent::Resize: return "QEvent::Resize - Widget's size changed (QResizeEvent)."; case QEvent::Resize: return "QEvent::Resize - Widget's size changed (QResizeEvent).";
case QEvent::ScrollPrepare: return "QEvent::ScrollPrepare - The object needs to fill in its geometry information (QScrollPrepareEvent)."; case QEvent::ScrollPrepare: return "QEvent::ScrollPrepare - The object needs to fill in its geometry information (QScrollPrepareEvent).";
@ -942,12 +804,6 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
URL_VIEW = 0, URL_VIEW = 0,
PROGRESS_VIEW PROGRESS_VIEW
}; };
enum RunStatus {
STATUS_NONE = 0,
STATUS_RUNNING,
STATUS_SUCCESS,
STATUS_ERROR
};
private: private:
QString _filename; QString _filename;
QWebElement _lastFocused; // cache for last focussed element QWebElement _lastFocused; // cache for last focussed element

@ -10,6 +10,9 @@
<height>1180</height> <height>1180</height>
</rect> </rect>
</property> </property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string/> <string/>
</property> </property>
@ -101,7 +104,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>888</width> <width>888</width>
<height>30</height> <height>34</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuViews"> <widget class="QMenu" name="menuViews">
@ -115,14 +118,13 @@
</property> </property>
<addaction name="_actionOpen"/> <addaction name="_actionOpen"/>
<addaction name="_actionOpenSetupScript"/> <addaction name="_actionOpenSetupScript"/>
<addaction name="separator"/>
<addaction name="_actionSave"/> <addaction name="_actionSave"/>
<addaction name="_actionSaveAs"/> <addaction name="_actionSaveAs"/>
<addaction name="separator"/>
<addaction name="_actionRun"/>
<addaction name="_actionRunLine"/>
<addaction name="separator"/>
<addaction name="_actionRevertToSaved"/> <addaction name="_actionRevertToSaved"/>
<addaction name="_actionClear"/> <addaction name="_actionClear"/>
<addaction name="_actionRun"/>
<addaction name="separator"/>
<addaction name="_actionQuit"/> <addaction name="_actionQuit"/>
</widget> </widget>
<widget class="QMenu" name="menuHelp"> <widget class="QMenu" name="menuHelp">
@ -138,7 +140,7 @@
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="_domDock"> <widget class="QDockWidget" name="_domDock">
<property name="windowTitle"> <property name="windowTitle">
<string>D&amp;OM Tree</string> <string>DOM &amp;Tree</string>
</property> </property>
<attribute name="dockWidgetArea"> <attribute name="dockWidgetArea">
<number>2</number> <number>2</number>
@ -393,144 +395,6 @@ this.dispatchEvent(evObj);</string>
</layout> </layout>
</widget> </widget>
</widget> </widget>
<widget class="QDockWidget" name="_scriptDock">
<property name="windowTitle">
<string>&amp;Test Script</string>
</property>
<attribute name="dockWidgetArea">
<number>4</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_12">
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="CodeEditor" name="_testscript"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QPushButton" name="_record">
<property name="text">
<string>Record</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="_run">
<property name="text">
<string>Run</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="_screenshots">
<property name="text">
<string>Screenshots</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QStackedWidget" name="_status">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_6"/>
<widget class="QWidget" name="page_5">
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;div style=&quot;font-size: xx-large&quot;&gt;⌛&lt;/div&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;div style=&quot;font-size: xx-large; color: green&quot;&gt;✔&lt;/div&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;div style=&quot;font-size: xx-large; color: red&quot;&gt;✘&lt;/div&gt;</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="_progress">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="_logDock"> <widget class="QDockWidget" name="_logDock">
<property name="windowTitle"> <property name="windowTitle">
<string>Scri&amp;pt Run Log</string> <string>Scri&amp;pt Run Log</string>
@ -699,6 +563,9 @@ this.dispatchEvent(evObj);</string>
</property> </property>
</action> </action>
<action name="_actionSaveAs"> <action name="_actionSaveAs">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Save &amp;As ...</string> <string>Save &amp;As ...</string>
</property> </property>
@ -715,6 +582,9 @@ this.dispatchEvent(evObj);</string>
</property> </property>
</action> </action>
<action name="_actionRun"> <action name="_actionRun">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>&amp;Run</string> <string>&amp;Run</string>
</property> </property>
@ -728,6 +598,9 @@ this.dispatchEvent(evObj);</string>
</property> </property>
</action> </action>
<action name="_actionClear"> <action name="_actionClear">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>&amp;Clear</string> <string>&amp;Clear</string>
</property> </property>
@ -986,38 +859,6 @@ this.dispatchEvent(evObj);</string>
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>_actionTestScript</sender>
<signal>triggered(bool)</signal>
<receiver>_scriptDock</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>443</x>
<y>155</y>
</hint>
</hints>
</connection>
<connection>
<sender>_scriptDock</sender>
<signal>visibilityChanged(bool)</signal>
<receiver>_actionTestScript</receiver>
<slot>setChecked(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>443</x>
<y>155</y>
</hint>
<hint type="destinationlabel">
<x>-1</x>
<y>-1</y>
</hint>
</hints>
</connection>
<connection> <connection>
<sender>_actionLog</sender> <sender>_actionLog</sender>
<signal>triggered(bool)</signal> <signal>triggered(bool)</signal>
@ -1066,22 +907,6 @@ this.dispatchEvent(evObj);</string>
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>_actionRun</sender>
<signal>triggered()</signal>
<receiver>_run</receiver>
<slot>click()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>299</x>
<y>90</y>
</hint>
</hints>
</connection>
<connection> <connection>
<sender>_url</sender> <sender>_url</sender>
<signal>activated(int)</signal> <signal>activated(int)</signal>
@ -1098,21 +923,5 @@ this.dispatchEvent(evObj);</string>
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>_testscript</sender>
<signal>modificationChanged(bool)</signal>
<receiver>TestGUI</receiver>
<slot>setWindowModified(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>126</x>
<y>144</y>
</hint>
<hint type="destinationlabel">
<x>443</x>
<y>589</y>
</hint>
</hints>
</connection>
</connections> </connections>
</ui> </ui>

@ -4,22 +4,22 @@
#include <version.hxx> #include <version.hxx>
int main(int argc, char *argv[]) try { int main(int argc, char *argv[]) try {
QApplication a(argc, argv); QApplication app(argc, argv);
a.setApplicationDisplayName(a.tr("WebTester")); app.setApplicationDisplayName(app.tr("WebTester"));
a.setApplicationName(webtester::package_name().c_str()); app.setApplicationName(webtester::package_name().c_str());
a.setApplicationVersion(webtester::version().c_str()); app.setApplicationVersion(webtester::version().c_str());
QCommandLineParser parser; QCommandLineParser parser;
parser.addHelpOption(); parser.addHelpOption();
parser.addOption(QCommandLineOption parser.addOption(QCommandLineOption
(QStringList()<<"u"<<"url", (QStringList()<<"u"<<"url",
"set initial URL to <url>", "url")); "set initial URL to <url>", "url"));
parser.process(a); parser.process(app);
QStringList scripts(parser.positionalArguments()); QStringList scripts(parser.positionalArguments());
TestGUI w(0, parser.value("url"), TestGUI win(0, parser.value("url"),
scripts.size()>1?scripts[0]:"", scripts.size()>1?scripts[0]:"",
scripts.size()>1?scripts[1]:scripts.size()?scripts[0]:""); scripts.size()>1?scripts[1]:scripts.size()?scripts[0]:"");
w.show(); win.show();
return a.exec(); return app.exec();
} catch (std::exception &x) { } catch (std::exception &x) {
std::cerr<<"**** error: "<<x.what()<<std::endl; std::cerr<<"**** error: "<<x.what()<<std::endl;
return 1; return 1;

Loading…
Cancel
Save