new option for timeout, longer timeout in shell, new untested command auth to set basic authentication credentials

master
Marc Wäckerlin 7 years ago
parent aa2fc7a98b
commit 6e8e665da7
  1. 2
      COPYING
  2. 2
      INSTALL
  3. 33
      README
  4. 1
      configure.ac
  5. 233
      src/commands.hxx
  6. 6
      src/networkaccessmanager.hxx
  7. 1
      src/testgui.hxx
  8. 6
      src/webrunner.cxx

@ -1 +1 @@
/usr/share/automake-1.15/COPYING /usr/share/automake-1.14/COPYING

@ -1 +1 @@
/usr/share/automake-1.15/INSTALL /usr/share/automake-1.14/INSTALL

@ -1,18 +1,25 @@
Test Your Web Application: GUI Web Testing Environment + Script Runner # Framework for Automated Web Application Testing
Webtester consists of two binaries: webtester to interactively create your web application tests and webrunner to run your test scripts.
There is a test GUI including browser to record user input while he surfs on the web and a test runner to run (recorded) test scripts. The tests can be integrated e.g. in a jenkins build job. It has been tested on Wordpress, Dokuwiki and Joomla pages. Joomla is difficult due to Javascript-Moo-Tools pollution. There's some specific support, that may help a bit, but to test Joomla sites, you need a lot of experience. Concluson: Avoid Joomla. There is a test GUI including browser to record user input while he surfs on the web and a test runner to run (recorded) test scripts. The tests can be integrated e.g. in a jenkins build job. It has been tested on Wordpress, Dokuwiki and Joomla pages. Joomla is difficult due to Javascript-Moo-Tools pollution. There's some specific support, that may help a bit, but to test Joomla sites, you need a lot of experience. Concluson: Avoid Joomla.
Sample Script to search my old homepage on Google, klick on the link, there click on tab «Computer» and check the title for the text «Marcs Computerblog»: Sample Script to search my old homepage on Google, klick on the link, there click on tab «Computer» and check the title for the text «Marcs Computerblog»:
load https://google.com load https://google.com
expect load expect loadStarted
setvalue input[name="q"] -> 'Marc Wäckerlin'; expect urlChanged
click input[name="btnG"] expect loadFinished true
expect load do input[name="q"]
click a[href^="/url?q=https://marc.waeckerlin.org/&"] this.value='Marc Wäckerlin';
expect load https://marc.waeckerlin.org/doku.php click input[name="btnG"]
click a[href="/computer/index"] expect loadStarted
expect load https://marc.waeckerlin.org/computer/index expect urlChanged
exists h1.sectionedit1 -> Marcs Computerblog expect loadFinished true
click a[href^="/url?q=https://marc.waeckerlin.org/&"]
expect loadStarted
expect urlChanged https://marc.waeckerlin.org/doku.php
expect loadFinished true
click a[href="/computer/index"]
expect loadStarted
expect urlChanged https://marc.waeckerlin.org/computer/index
expect loadFinished true
exists h1.sectionedit1 -> Marcs Computerblog

@ -41,6 +41,7 @@ AM_CPPFLAGS="${AM_CPPFLAGS} -DQT_NO_KEYWORDS"
# libraries used # libraries used
AX_PKG_REQUIRE([mrwcxx], [mrw-c++]) AX_PKG_REQUIRE([mrwcxx], [mrw-c++])
AX_PKG_REQUIRE([xmlcxx], [libxml-cxx]) AX_PKG_REQUIRE([xmlcxx], [libxml-cxx])
AC_CHECK_HEADERS([cxxabi.h])
# create output # create output
AC_OUTPUT AC_OUTPUT

@ -10,6 +10,7 @@
#include <exceptions.hxx> #include <exceptions.hxx>
#include <webpage.hxx> #include <webpage.hxx>
#include <QNetworkReply> #include <QNetworkReply>
#include <QAuthenticator>
#include <QCoreApplication> #include <QCoreApplication>
#include <QStringList> #include <QStringList>
#include <QWebFrame> #include <QWebFrame>
@ -32,6 +33,20 @@
#include <cassert> #include <cassert>
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#ifdef HAVE_CXXABI_H
#include <cxxabi.h>
inline QString demangle(const char* mangled) {
int status;
std::unique_ptr<char[], void (*)(void*)> result(
abi::__cxa_demangle(mangled, 0, 0, &status), free);
return QString(result.get() ? result.get() : mangled);
}
#else
inline QString demangle(const char* mangled) {
return QString(mangled);
}
#endif
namespace std { namespace std {
template<typename T> class optional { template<typename T> class optional {
private: private:
@ -99,7 +114,7 @@ class Command: public QObject {
QStringList&, QString, int, int) = 0; QStringList&, QString, int, int) = 0;
virtual bool execute(Script*, QWebFrame*) = 0; virtual bool execute(Script*, QWebFrame*) = 0;
static void error(Logger& log, const Exception& e) { static void error(Logger& log, const Exception& e) {
log(QString(" FAILED: ")+e.what()); log(QString(" FAILED[")+demangle(typeid(e).name())+"]: "+e.what());
throw e; throw e;
} }
void line(int linenr) { void line(int linenr) {
@ -187,20 +202,38 @@ class Command: public QObject {
} }
return commands; return commands;
} }
QStringList commaSeparatedList(QString value) { QStringList quotedStrings(QString value,
value=value.trimmed(); QString delimiter = " ",
if (!value.size()) return QStringList(); bool keepDelimiters = false) {
switch (value.size()>1&&value.at(0)==value.at(value.size()-1) QStringList res;
?value.at(0).toLatin1():'\0') { QString quot("'\"");
case '"': case '\'': { while (value=value.trimmed(), value.size()) {
return value.mid(1, value.size()-2) QRegularExpression re;
.split(QRegularExpression(QString(value[0])+", *" int start(0);
+QString(value[0]))); if (quot.contains(value[0])) {
} break; re = QRegularExpression(value[0]+" *(("+delimiter+" *)|$)");
default: { start = 1;
return value.split(QRegularExpression(", *")); } else {
re = QRegularExpression("( *"+delimiter+" *)|$");
} }
int pos(value.indexOf(re, start));
if (pos<start) throw BadArgument("quote missmatch in "+value);
QRegularExpressionMatch m(re.match(value, start));
res += value.mid(start, m.capturedStart()-start);
value.remove(0, m.capturedEnd());
if (keepDelimiters && m.capturedLength()) res+=m.captured().mid(start).trimmed();
std::cout<<"REMOVE: \""<<m.captured()<<"\" 0 - "<<(m.capturedEnd()+start)
<<" start="<<start<<" pos="<<pos<<std::endl
<<"REMAINING: \""<<value<<"\""<<std::endl;
}
std::cout<<"FOUND"<<std::endl;
Q_FOREACH(QString tag, res) {
std::cout<<" - \""<<tag<<"\""<<std::endl;
} }
return res;
}
QStringList commaSeparatedList(QString value) {
return quotedStrings(value, ",");
} }
static QWebElement find(QWebFrame* frame, QString selector, static QWebElement find(QWebFrame* frame, QString selector,
int repeat = 2, int sleepsec = 1) { int repeat = 2, int sleepsec = 1) {
@ -376,15 +409,15 @@ class RunDownload: public QObject {
public: public:
RunDownload(QNetworkReply* reply, QString filename): RunDownload(QNetworkReply* reply, QString filename):
_reply(reply), _file(filename) { _reply(reply), _file(filename) {
connect(_reply, SIGNAL(finished()), SLOT(finished())); assert(connect(_reply, SIGNAL(finished()), SLOT(finished())));
connect(_reply, SIGNAL(downloadProgress(qint64, qint64)), assert(connect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
SLOT(downloadProgress(qint64, qint64))); SLOT(downloadProgress(qint64, qint64))));
_file.open(QIODevice::WriteOnly); _file.open(QIODevice::WriteOnly);
} }
~RunDownload() { ~RunDownload() {
disconnect(_reply, SIGNAL(finished()), this, SLOT(finished())); assert(disconnect(_reply, SIGNAL(finished()), this, SLOT(finished())));
disconnect(_reply, SIGNAL(downloadProgress(qint64, qint64)), assert(disconnect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(downloadProgress(qint64, qint64))); this, SLOT(downloadProgress(qint64, qint64))));
delete _reply; delete _reply;
} }
Q_SIGNALS: Q_SIGNALS:
@ -454,7 +487,8 @@ class Script: public QObject {
.replace("&nbsp;", " ").replace("&amp;", "&"); .replace("&nbsp;", " ").replace("&amp;", "&");
} }
public: public:
Script(): _clicktype(JAVASCRIPT_CLICK), _command(0), _screenshots(true) { Script(): _clicktype(JAVASCRIPT_CLICK), _command(0), _screenshots(true),
_defaultTimeout(20) {
initPrototypes(); initPrototypes();
} }
Script(const Script& o): Script(const Script& o):
@ -535,7 +569,7 @@ class Script: public QObject {
_variables.clear(); _variables.clear();
_rvariables.clear(); _rvariables.clear();
_functions.clear(); _functions.clear();
_timeout = 20; _timeout = _defaultTimeout;
_clicktype = JAVASCRIPT_CLICK; _clicktype = JAVASCRIPT_CLICK;
} }
std::shared_ptr<Command> parseLine(QStringList& in, std::shared_ptr<Command> parseLine(QStringList& in,
@ -586,7 +620,7 @@ class Script: public QObject {
int maxretries = 0) { int maxretries = 0) {
bool res(true); bool res(true);
_testsuites = testsuites; _testsuites = testsuites;
_timeout = 20; // defaults to 20s _timeout = _defaultTimeout; // defaults to 20s
_ignoreSignalsUntil.clear(); _ignoreSignalsUntil.clear();
addSignals(frame); addSignals(frame);
_screenshots = screenshots; _screenshots = screenshots;
@ -815,6 +849,7 @@ class Script: public QObject {
_variables = o._variables; _variables = o._variables;
_rvariables = o._rvariables; _rvariables = o._rvariables;
_timeout = o._timeout; _timeout = o._timeout;
_defaultTimeout = o._timeout;
_clicktype = o._clicktype; _clicktype = o._clicktype;
_testsuites = o._testsuites; _testsuites = o._testsuites;
_testclass = o._testclass; _testclass = o._testclass;
@ -845,6 +880,15 @@ class Script: public QObject {
void timeout(int t) { void timeout(int t) {
_timeout = t; _timeout = t;
} }
void defaultTimeout(int t) {
_defaultTimeout = t;
}
void auth(const QString& realm, const QString& username, const QString& password) {
if (!username.isEmpty() && !password.isEmpty())
_auth[realm] = {username, password};
else if (_auth.contains(realm))
_auth.erase(_auth.find(realm));
}
void clicktype(ClickType c) { void clicktype(ClickType c) {
_clicktype = c; _clicktype = c;
} }
@ -876,33 +920,39 @@ class Script: public QObject {
return QString(); return QString();
} }
void addSignals(QWebFrame* frame) { void addSignals(QWebFrame* frame) {
connect(dynamic_cast<NetworkAccessManager*> assert(connect(dynamic_cast<NetworkAccessManager*>
(frame->page()->networkAccessManager()), (frame->page()->networkAccessManager()),
SIGNAL(log(QString)), SIGNAL(log(QString)),
SLOT(log(QString))); SLOT(log(QString))));
connect(frame, SIGNAL(contentsSizeChanged(const QSize&)), assert(connect(frame->page()->networkAccessManager(),
SLOT(contentsSizeChanged(const QSize&))); SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
connect(frame, SIGNAL(iconChanged()), SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*))));
SLOT(iconChanged())); assert(connect(frame, SIGNAL(contentsSizeChanged(const QSize&)),
connect(frame, SIGNAL(initialLayoutCompleted()), SLOT(contentsSizeChanged(const QSize&))));
SLOT(initialLayoutCompleted())); assert(connect(frame, SIGNAL(iconChanged()),
connect(frame, SIGNAL(javaScriptWindowObjectCleared()), SLOT(iconChanged())));
SLOT(javaScriptWindowObjectCleared())); assert(connect(frame, SIGNAL(initialLayoutCompleted()),
connect(frame, SIGNAL(loadFinished(bool)), SLOT(initialLayoutCompleted())));
SLOT(loadFinished(bool))); assert(connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
connect(frame, SIGNAL(loadStarted()), SLOT(javaScriptWindowObjectCleared())));
SLOT(loadStarted())); assert(connect(frame, SIGNAL(loadFinished(bool)),
connect(frame, SIGNAL(titleChanged(const QString&)), SLOT(loadFinished(bool))));
SLOT(titleChanged(const QString&))); assert(connect(frame, SIGNAL(loadStarted()),
connect(frame, SIGNAL(urlChanged(const QUrl&)), SLOT(loadStarted())));
SLOT(urlChanged(const QUrl&))); assert(connect(frame, SIGNAL(titleChanged(const QString&)),
connect(&_timer, SIGNAL(timeout()), SLOT(timeout())); SLOT(titleChanged(const QString&))));
assert(connect(frame, SIGNAL(urlChanged(const QUrl&)),
SLOT(urlChanged(const QUrl&))));
assert(connect(&_timer, SIGNAL(timeout()), SLOT(timeout())));
} }
void removeSignals(QWebFrame* frame) { void removeSignals(QWebFrame* frame) {
disconnect(dynamic_cast<NetworkAccessManager*> disconnect(dynamic_cast<NetworkAccessManager*>
(frame->page()->networkAccessManager()), (frame->page()->networkAccessManager()),
SIGNAL(log(QString)), SIGNAL(log(QString)),
this, SLOT(log(QString))); this, SLOT(log(QString)));
disconnect(frame->page()->networkAccessManager(),
SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*)));
disconnect(frame, SIGNAL(contentsSizeChanged(const QSize&)), disconnect(frame, SIGNAL(contentsSizeChanged(const QSize&)),
this, SLOT(contentsSizeChanged(const QSize&))); this, SLOT(contentsSizeChanged(const QSize&)));
disconnect(frame, SIGNAL(iconChanged()), disconnect(frame, SIGNAL(iconChanged()),
@ -931,9 +981,11 @@ class Script: public QObject {
for (QChar& c: text) if (c<32&&c!='\n') c='?'; for (QChar& c: text) if (c<32&&c!='\n') c='?';
if (cmd) if (cmd)
prefix += QString("%2:%3%1 ") prefix += QString("%2:%3%1 ")
.arg(QString(cmd->indent(), QChar(' '))) .arg(QString(cmd->indent()+2, QChar(' ')))
.arg(cmd->file(), 20, QChar(' ')) .arg(cmd->file(), 20, QChar(' '))
.arg(cmd->line(), -4, 10, QChar(' ')); .arg(cmd->line(), -4, 10, QChar(' '));
else
prefix += " .... ";
text = prefix+text.split('\n').join("\n"+prefix+" "); text = prefix+text.split('\n').join("\n"+prefix+" ");
logging(text); logging(text);
std::cout<<text<<std::endl<<std::flush; std::cout<<text<<std::endl<<std::flush;
@ -950,7 +1002,7 @@ class Script: public QObject {
} }
private: private:
void error(const Exception& e) { void error(const Exception& e) {
log(QString(" FAILED: ")+e.what()); log(QString(" FAILED[")+demangle(typeid(e).name())+"]: "+e.what());
throw e; throw e;
} }
std::shared_ptr<Command> unknown(QString command) { std::shared_ptr<Command> unknown(QString command) {
@ -965,6 +1017,15 @@ class Script: public QObject {
_prototypes[c->tag()] = std::shared_ptr<Command>(c); _prototypes[c->tag()] = std::shared_ptr<Command>(c);
} }
private Q_SLOTS: private Q_SLOTS:
void authenticationRequired(QNetworkReply*, QAuthenticator* a) {
if (_auth.contains(a->realm())) {
log("network: login to "+a->realm());
a->setUser(_auth[a->realm()].username);
a->setPassword(_auth[a->realm()].password);
} else {
log("network: no credentials for "+a->realm());
}
}
void contentsSizeChanged(const QSize&) { void contentsSizeChanged(const QSize&) {
} }
void iconChanged() { void iconChanged() {
@ -1012,6 +1073,10 @@ class Script: public QObject {
error(TimeOut()); error(TimeOut());
} }
private: private:
struct AuthRealm {
QString username;
QString password;
};
typedef std::map<QString, std::shared_ptr<Command>> Prototypes; typedef std::map<QString, std::shared_ptr<Command>> Prototypes;
typedef std::vector<std::shared_ptr<Command>> Commands; typedef std::vector<std::shared_ptr<Command>> Commands;
Prototypes _prototypes; Prototypes _prototypes;
@ -1028,12 +1093,14 @@ class Script: public QObject {
QMap<LenString, LenString> _rvariables; ///< reverse variable mapping QMap<LenString, LenString> _rvariables; ///< reverse variable mapping
QMap<QString, std::shared_ptr<Function> > _functions; QMap<QString, std::shared_ptr<Function> > _functions;
int _timeout; int _timeout;
int _defaultTimeout;
ClickType _clicktype; ClickType _clicktype;
QString _targetdir; QString _targetdir;
std::shared_ptr<xml::Node> _testsuites; ///< only valid within run std::shared_ptr<xml::Node> _testsuites; ///< only valid within run
QString _testclass; QString _testclass;
Command* _command; Command* _command;
QString _path; QString _path;
QMap<QString, AuthRealm> _auth;
}; };
class Do: public Command { class Do: public Command {
@ -1587,8 +1654,8 @@ class Download: public Command {
_realfilename = script->replacevars(_filename); _realfilename = script->replacevars(_filename);
log("REALFILENAME="+_realfilename); log("REALFILENAME="+_realfilename);
frame->page()->setForwardUnsupportedContent(true); frame->page()->setForwardUnsupportedContent(true);
connect(frame->page(), SIGNAL(unsupportedContent(QNetworkReply*)), assert(connect(frame->page(), SIGNAL(unsupportedContent(QNetworkReply*)),
this, SLOT(unsupportedContent(QNetworkReply*))); this, SLOT(unsupportedContent(QNetworkReply*))));
try { try {
bool res(_next->execute(script, frame)); // start download bool res(_next->execute(script, frame)); // start download
script->timer().stop(); // no timeout during download script->timer().stop(); // no timeout during download
@ -1626,8 +1693,8 @@ class Download: public Command {
} }
} }
} }
connect(new RunDownload(reply, _realfilename), assert(connect(new RunDownload(reply, _realfilename),
SIGNAL(completed(bool, bool)), SLOT(completed(bool, bool))); SIGNAL(completed(bool, bool)), SLOT(completed(bool, bool))));
} }
private: private:
QString _filename; QString _filename;
@ -2312,14 +2379,22 @@ class Check: public Command {
int indent) { int indent) {
std::shared_ptr<Check> cmd(new Check()); std::shared_ptr<Check> cmd(new Check());
cmd->_next = 0; cmd->_next = 0;
int pos(args.indexOf(QRegularExpression("[=!.^~<>]"))); QString comp("[=!.^~<>]");
if (pos<0) throw BadArgument(tag()+" needs a comparision, not: "+args); QStringList allargs = quotedStrings(args, comp, true);
cmd->_value1 = args.left(pos).trimmed(); if (allargs.size()<2 || allargs[1].size()!=1 ||
cmd->_cmp = args[pos].toLatin1(); !QRegularExpression("^"+comp+"$").match(allargs[1]).hasMatch())
cmd->_value2 = args.mid(pos+1).trimmed(); throw BadArgument(tag()+" needs a comparision, not: "+args);
if (in.size() && in.first().contains(QRegularExpression("^ "))) { if (allargs.size()>3)
cmd->_next = script->parseLine(in, file, line+1, indent+1); throw BadArgument(tag()+" has at most three arguments");
cmd->_next->log(false); // suppress logging of subcommand cmd->_value1 = allargs[0];
cmd->_cmp = allargs[1][0].toLatin1();
if (allargs.size()==3) {
cmd->_value2 = allargs[2];
} else {
if (in.size() && in.first().contains(QRegularExpression("^ "))) {
cmd->_next = script->parseLine(in, file, line+1, indent+1);
cmd->_next->log(false); // suppress logging of subcommand
} else throw BadArgument(tag()+" needs a third argument or a following command");
} }
return cmd; return cmd;
} }
@ -2704,6 +2779,49 @@ class Fail: public Command {
QString _text; QString _text;
}; };
class Auth: public Command {
public:
QString tag() const {
return "auth";
}
QString description() const {
return
tag()+" <realm> <username> <password>"
"\n\n"+
tag()+" <realm>"
"\n\n"
"Set basic authentication credentials for <realm> to"
" <username> and <password>. If no realm is given,"
" the credentials for the given realm are removed.";
}
QString command() const {
return tag()+" "+_username+" "+_password;
}
std::shared_ptr<Command> parse(Script*, QString args,
QStringList&, QString, int, int) {
std::shared_ptr<Auth> cmd(new Auth());
QStringList allargs = args.split(" ");
if (!allargs.size()) throw BadArgument("requires at least a <realm>");
cmd->_realm = allargs.takeFirst();
if (allargs.size() && allargs.size()==2) {
cmd->_username=allargs[0];
cmd->_password=allargs[1];
} else {
throw BadArgument(QString("requires <username> and <password>, but %1 was given")
.arg(args));
}
return cmd;
}
bool execute(Script* script, QWebFrame* frame) {
Logger log(this, script);
script->auth(_realm, _username, _password);
return true;
}
private:
QString _realm;
QString _username;
QString _password;
};
/* Template: /* Template:
class : public Command { class : public Command {
@ -2786,8 +2904,8 @@ inline bool Command::runScript(Logger& log, Command* parentCommand,
scriptCopy.set(*var, parent->replacevars(*arg)); scriptCopy.set(*var, parent->replacevars(*arg));
} }
try { try {
connect(&scriptCopy, SIGNAL(logging(QString)), assert(connect(&scriptCopy, SIGNAL(logging(QString)),
parent, SLOT(parentlog(QString))); parent, SLOT(parentlog(QString))));
parent->removeSignals(frame); parent->removeSignals(frame);
bool res(scriptCopy.run(frame)); bool res(scriptCopy.run(frame));
parent->addSignals(frame); parent->addSignals(frame);
@ -2845,6 +2963,7 @@ inline void Script::initPrototypes() {
add(new Include); add(new Include);
add(new Case); add(new Case);
add(new Fail); add(new Fail);
add(new Auth);
} }
#endif #endif

@ -60,13 +60,13 @@ class NetworkAccessManager: public QNetworkAccessManager {
return QNetworkAccessManager::configuration(); return QNetworkAccessManager::configuration();
} }
void connectToHost(const QString& hostName, quint16 port = 80) { void connectToHost(const QString& hostName, quint16 port = 80) {
//log(__PRETTY_FUNCTION__); //log(__PRETTY_FUNCTION__ + QString(" -> ") + hostName);
QNetworkAccessManager::connectToHost(hostName, port); QNetworkAccessManager::connectToHost(hostName, port);
} }
void connectToHostEncrypted(const QString& hostName, quint16 port = 443, void connectToHostEncrypted(const QString& hostName, quint16 port = 443,
const QSslConfiguration& sslConfiguration const QSslConfiguration& sslConfiguration
= QSslConfiguration::defaultConfiguration()) { = QSslConfiguration::defaultConfiguration()) {
//log(__PRETTY_FUNCTION__); //log(__PRETTY_FUNCTION__ + QString(" -> ") + hostName);
QNetworkAccessManager::connectToHostEncrypted(hostName, port, QNetworkAccessManager::connectToHostEncrypted(hostName, port,
sslConfiguration); sslConfiguration);
} }
@ -171,7 +171,7 @@ class NetworkAccessManager: public QNetworkAccessManager {
virtual QNetworkReply* createRequest(Operation op, virtual QNetworkReply* createRequest(Operation op,
const QNetworkRequest& req, const QNetworkRequest& req,
QIODevice* outgoingData = 0) { QIODevice* outgoingData = 0) {
//log(__PRETTY_FUNCTION__); //log(__PRETTY_FUNCTION__ + QString(" -> ") + req.url().url());
switch (op) { switch (op) {
case QNetworkAccessManager::HeadOperation: break; case QNetworkAccessManager::HeadOperation: break;
case QNetworkAccessManager::GetOperation: break; case QNetworkAccessManager::GetOperation: break;

@ -195,6 +195,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
//std::cout<<"titleChanged: "<<title.toStdString()<<std::endl; //std::cout<<"titleChanged: "<<title.toStdString()<<std::endl;
} }
void on__web_urlChanged(const QUrl& url) { void on__web_urlChanged(const QUrl& url) {
_url->setText(url.url());
enterText(true); enterText(true);
if (_record->isChecked()) if (_record->isChecked())
appendCommand("expect "+map("urlChanged "+url.url())); appendCommand("expect "+map("urlChanged "+url.url()));

@ -115,6 +115,10 @@ int main(int argc, char *argv[]) try {
(QStringList()<<"r"<<"retries", (QStringList()<<"r"<<"retries",
"on error retry up to <maxretries> times", "on error retry up to <maxretries> times",
"maxretries", "0")); "maxretries", "0"));
parser.addOption(QCommandLineOption
(QStringList()<<"timeout",
"set default timeout in seconds",
"timeout", "40"));
parser.addOption(QCommandLineOption parser.addOption(QCommandLineOption
(QStringList()<<"W"<<"width", (QStringList()<<"W"<<"width",
"set screenshot size to <width> pixel", "width", "2048")); "set screenshot size to <width> pixel", "width", "2048"));
@ -141,8 +145,10 @@ int main(int argc, char *argv[]) try {
} }
if (parser.isSet("path")) script.path(parser.value("path")); if (parser.isSet("path")) script.path(parser.value("path"));
int retries(parser.value("retries").toInt()); int retries(parser.value("retries").toInt());
int timeout(parser.value("timeout").toInt());
int width(parser.value("width").toInt()); int width(parser.value("width").toInt());
int height(parser.value("height").toInt()); int height(parser.value("height").toInt());
script.defaultTimeout(parser.value("timeout").toInt());
QString target(parser.value("target-path")); QString target(parser.value("target-path"));
p.resize(width, height); p.resize(width, height);
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuites")); std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuites"));

Loading…
Cancel
Save