new option for timeout, longer timeout in shell, new untested command auth to set basic authentication credentials
This commit is contained in:
33
README
33
README
@@ -1,18 +1,25 @@
|
||||
Test Your Web Application: GUI Web Testing Environment + Script Runner
|
||||
|
||||
Webtester consists of two binaries: webtester to interactively create your web application tests and webrunner to run your test scripts.
|
||||
# Framework for Automated Web Application Testing
|
||||
|
||||
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»:
|
||||
|
||||
load https://google.com
|
||||
expect load
|
||||
setvalue input[name="q"] -> 'Marc Wäckerlin';
|
||||
click input[name="btnG"]
|
||||
expect load
|
||||
click a[href^="/url?q=https://marc.waeckerlin.org/&"]
|
||||
expect load https://marc.waeckerlin.org/doku.php
|
||||
click a[href="/computer/index"]
|
||||
expect load https://marc.waeckerlin.org/computer/index
|
||||
exists h1.sectionedit1 -> Marcs Computerblog
|
||||
load https://google.com
|
||||
expect loadStarted
|
||||
expect urlChanged
|
||||
expect loadFinished true
|
||||
do input[name="q"]
|
||||
this.value='Marc Wäckerlin';
|
||||
click input[name="btnG"]
|
||||
expect loadStarted
|
||||
expect urlChanged
|
||||
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
|
||||
AX_PKG_REQUIRE([mrwcxx], [mrw-c++])
|
||||
AX_PKG_REQUIRE([xmlcxx], [libxml-cxx])
|
||||
AC_CHECK_HEADERS([cxxabi.h])
|
||||
|
||||
# create output
|
||||
AC_OUTPUT
|
||||
|
233
src/commands.hxx
233
src/commands.hxx
@@ -10,6 +10,7 @@
|
||||
#include <exceptions.hxx>
|
||||
#include <webpage.hxx>
|
||||
#include <QNetworkReply>
|
||||
#include <QAuthenticator>
|
||||
#include <QCoreApplication>
|
||||
#include <QStringList>
|
||||
#include <QWebFrame>
|
||||
@@ -32,6 +33,20 @@
|
||||
#include <cassert>
|
||||
#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 {
|
||||
template<typename T> class optional {
|
||||
private:
|
||||
@@ -99,7 +114,7 @@ class Command: public QObject {
|
||||
QStringList&, QString, int, int) = 0;
|
||||
virtual bool execute(Script*, QWebFrame*) = 0;
|
||||
static void error(Logger& log, const Exception& e) {
|
||||
log(QString(" FAILED: ")+e.what());
|
||||
log(QString(" FAILED[")+demangle(typeid(e).name())+"]: "+e.what());
|
||||
throw e;
|
||||
}
|
||||
void line(int linenr) {
|
||||
@@ -187,20 +202,38 @@ class Command: public QObject {
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
QStringList commaSeparatedList(QString value) {
|
||||
value=value.trimmed();
|
||||
if (!value.size()) return QStringList();
|
||||
switch (value.size()>1&&value.at(0)==value.at(value.size()-1)
|
||||
?value.at(0).toLatin1():'\0') {
|
||||
case '"': case '\'': {
|
||||
return value.mid(1, value.size()-2)
|
||||
.split(QRegularExpression(QString(value[0])+", *"
|
||||
+QString(value[0])));
|
||||
} break;
|
||||
default: {
|
||||
return value.split(QRegularExpression(", *"));
|
||||
QStringList quotedStrings(QString value,
|
||||
QString delimiter = " ",
|
||||
bool keepDelimiters = false) {
|
||||
QStringList res;
|
||||
QString quot("'\"");
|
||||
while (value=value.trimmed(), value.size()) {
|
||||
QRegularExpression re;
|
||||
int start(0);
|
||||
if (quot.contains(value[0])) {
|
||||
re = QRegularExpression(value[0]+" *(("+delimiter+" *)|$)");
|
||||
start = 1;
|
||||
} 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,
|
||||
int repeat = 2, int sleepsec = 1) {
|
||||
@@ -376,15 +409,15 @@ class RunDownload: public QObject {
|
||||
public:
|
||||
RunDownload(QNetworkReply* reply, QString filename):
|
||||
_reply(reply), _file(filename) {
|
||||
connect(_reply, SIGNAL(finished()), SLOT(finished()));
|
||||
connect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
|
||||
SLOT(downloadProgress(qint64, qint64)));
|
||||
assert(connect(_reply, SIGNAL(finished()), SLOT(finished())));
|
||||
assert(connect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
|
||||
SLOT(downloadProgress(qint64, qint64))));
|
||||
_file.open(QIODevice::WriteOnly);
|
||||
}
|
||||
~RunDownload() {
|
||||
disconnect(_reply, SIGNAL(finished()), this, SLOT(finished()));
|
||||
disconnect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
|
||||
this, SLOT(downloadProgress(qint64, qint64)));
|
||||
assert(disconnect(_reply, SIGNAL(finished()), this, SLOT(finished())));
|
||||
assert(disconnect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
|
||||
this, SLOT(downloadProgress(qint64, qint64))));
|
||||
delete _reply;
|
||||
}
|
||||
Q_SIGNALS:
|
||||
@@ -454,7 +487,8 @@ class Script: public QObject {
|
||||
.replace(" ", " ").replace("&", "&");
|
||||
}
|
||||
public:
|
||||
Script(): _clicktype(JAVASCRIPT_CLICK), _command(0), _screenshots(true) {
|
||||
Script(): _clicktype(JAVASCRIPT_CLICK), _command(0), _screenshots(true),
|
||||
_defaultTimeout(20) {
|
||||
initPrototypes();
|
||||
}
|
||||
Script(const Script& o):
|
||||
@@ -535,7 +569,7 @@ class Script: public QObject {
|
||||
_variables.clear();
|
||||
_rvariables.clear();
|
||||
_functions.clear();
|
||||
_timeout = 20;
|
||||
_timeout = _defaultTimeout;
|
||||
_clicktype = JAVASCRIPT_CLICK;
|
||||
}
|
||||
std::shared_ptr<Command> parseLine(QStringList& in,
|
||||
@@ -586,7 +620,7 @@ class Script: public QObject {
|
||||
int maxretries = 0) {
|
||||
bool res(true);
|
||||
_testsuites = testsuites;
|
||||
_timeout = 20; // defaults to 20s
|
||||
_timeout = _defaultTimeout; // defaults to 20s
|
||||
_ignoreSignalsUntil.clear();
|
||||
addSignals(frame);
|
||||
_screenshots = screenshots;
|
||||
@@ -815,6 +849,7 @@ class Script: public QObject {
|
||||
_variables = o._variables;
|
||||
_rvariables = o._rvariables;
|
||||
_timeout = o._timeout;
|
||||
_defaultTimeout = o._timeout;
|
||||
_clicktype = o._clicktype;
|
||||
_testsuites = o._testsuites;
|
||||
_testclass = o._testclass;
|
||||
@@ -845,6 +880,15 @@ class Script: public QObject {
|
||||
void timeout(int 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) {
|
||||
_clicktype = c;
|
||||
}
|
||||
@@ -876,33 +920,39 @@ class Script: public QObject {
|
||||
return QString();
|
||||
}
|
||||
void addSignals(QWebFrame* frame) {
|
||||
connect(dynamic_cast<NetworkAccessManager*>
|
||||
assert(connect(dynamic_cast<NetworkAccessManager*>
|
||||
(frame->page()->networkAccessManager()),
|
||||
SIGNAL(log(QString)),
|
||||
SLOT(log(QString)));
|
||||
connect(frame, SIGNAL(contentsSizeChanged(const QSize&)),
|
||||
SLOT(contentsSizeChanged(const QSize&)));
|
||||
connect(frame, SIGNAL(iconChanged()),
|
||||
SLOT(iconChanged()));
|
||||
connect(frame, SIGNAL(initialLayoutCompleted()),
|
||||
SLOT(initialLayoutCompleted()));
|
||||
connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
|
||||
SLOT(javaScriptWindowObjectCleared()));
|
||||
connect(frame, SIGNAL(loadFinished(bool)),
|
||||
SLOT(loadFinished(bool)));
|
||||
connect(frame, SIGNAL(loadStarted()),
|
||||
SLOT(loadStarted()));
|
||||
connect(frame, SIGNAL(titleChanged(const QString&)),
|
||||
SLOT(titleChanged(const QString&)));
|
||||
connect(frame, SIGNAL(urlChanged(const QUrl&)),
|
||||
SLOT(urlChanged(const QUrl&)));
|
||||
connect(&_timer, SIGNAL(timeout()), SLOT(timeout()));
|
||||
SLOT(log(QString))));
|
||||
assert(connect(frame->page()->networkAccessManager(),
|
||||
SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
|
||||
SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*))));
|
||||
assert(connect(frame, SIGNAL(contentsSizeChanged(const QSize&)),
|
||||
SLOT(contentsSizeChanged(const QSize&))));
|
||||
assert(connect(frame, SIGNAL(iconChanged()),
|
||||
SLOT(iconChanged())));
|
||||
assert(connect(frame, SIGNAL(initialLayoutCompleted()),
|
||||
SLOT(initialLayoutCompleted())));
|
||||
assert(connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
|
||||
SLOT(javaScriptWindowObjectCleared())));
|
||||
assert(connect(frame, SIGNAL(loadFinished(bool)),
|
||||
SLOT(loadFinished(bool))));
|
||||
assert(connect(frame, SIGNAL(loadStarted()),
|
||||
SLOT(loadStarted())));
|
||||
assert(connect(frame, SIGNAL(titleChanged(const QString&)),
|
||||
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) {
|
||||
disconnect(dynamic_cast<NetworkAccessManager*>
|
||||
(frame->page()->networkAccessManager()),
|
||||
SIGNAL(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&)),
|
||||
this, SLOT(contentsSizeChanged(const QSize&)));
|
||||
disconnect(frame, SIGNAL(iconChanged()),
|
||||
@@ -931,9 +981,11 @@ class Script: public QObject {
|
||||
for (QChar& c: text) if (c<32&&c!='\n') c='?';
|
||||
if (cmd)
|
||||
prefix += QString("%2:%3%1 ")
|
||||
.arg(QString(cmd->indent(), QChar(' ')))
|
||||
.arg(QString(cmd->indent()+2, QChar(' ')))
|
||||
.arg(cmd->file(), 20, QChar(' '))
|
||||
.arg(cmd->line(), -4, 10, QChar(' '));
|
||||
else
|
||||
prefix += " .... ";
|
||||
text = prefix+text.split('\n').join("\n"+prefix+" ");
|
||||
logging(text);
|
||||
std::cout<<text<<std::endl<<std::flush;
|
||||
@@ -950,7 +1002,7 @@ class Script: public QObject {
|
||||
}
|
||||
private:
|
||||
void error(const Exception& e) {
|
||||
log(QString(" FAILED: ")+e.what());
|
||||
log(QString(" FAILED[")+demangle(typeid(e).name())+"]: "+e.what());
|
||||
throw e;
|
||||
}
|
||||
std::shared_ptr<Command> unknown(QString command) {
|
||||
@@ -965,6 +1017,15 @@ class Script: public QObject {
|
||||
_prototypes[c->tag()] = std::shared_ptr<Command>(c);
|
||||
}
|
||||
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 iconChanged() {
|
||||
@@ -1012,6 +1073,10 @@ class Script: public QObject {
|
||||
error(TimeOut());
|
||||
}
|
||||
private:
|
||||
struct AuthRealm {
|
||||
QString username;
|
||||
QString password;
|
||||
};
|
||||
typedef std::map<QString, std::shared_ptr<Command>> Prototypes;
|
||||
typedef std::vector<std::shared_ptr<Command>> Commands;
|
||||
Prototypes _prototypes;
|
||||
@@ -1028,12 +1093,14 @@ class Script: public QObject {
|
||||
QMap<LenString, LenString> _rvariables; ///< reverse variable mapping
|
||||
QMap<QString, std::shared_ptr<Function> > _functions;
|
||||
int _timeout;
|
||||
int _defaultTimeout;
|
||||
ClickType _clicktype;
|
||||
QString _targetdir;
|
||||
std::shared_ptr<xml::Node> _testsuites; ///< only valid within run
|
||||
QString _testclass;
|
||||
Command* _command;
|
||||
QString _path;
|
||||
QMap<QString, AuthRealm> _auth;
|
||||
};
|
||||
|
||||
class Do: public Command {
|
||||
@@ -1587,8 +1654,8 @@ class Download: public Command {
|
||||
_realfilename = script->replacevars(_filename);
|
||||
log("REALFILENAME="+_realfilename);
|
||||
frame->page()->setForwardUnsupportedContent(true);
|
||||
connect(frame->page(), SIGNAL(unsupportedContent(QNetworkReply*)),
|
||||
this, SLOT(unsupportedContent(QNetworkReply*)));
|
||||
assert(connect(frame->page(), SIGNAL(unsupportedContent(QNetworkReply*)),
|
||||
this, SLOT(unsupportedContent(QNetworkReply*))));
|
||||
try {
|
||||
bool res(_next->execute(script, frame)); // start download
|
||||
script->timer().stop(); // no timeout during download
|
||||
@@ -1626,8 +1693,8 @@ class Download: public Command {
|
||||
}
|
||||
}
|
||||
}
|
||||
connect(new RunDownload(reply, _realfilename),
|
||||
SIGNAL(completed(bool, bool)), SLOT(completed(bool, bool)));
|
||||
assert(connect(new RunDownload(reply, _realfilename),
|
||||
SIGNAL(completed(bool, bool)), SLOT(completed(bool, bool))));
|
||||
}
|
||||
private:
|
||||
QString _filename;
|
||||
@@ -2312,14 +2379,22 @@ class Check: public Command {
|
||||
int indent) {
|
||||
std::shared_ptr<Check> cmd(new Check());
|
||||
cmd->_next = 0;
|
||||
int pos(args.indexOf(QRegularExpression("[=!.^~<>]")));
|
||||
if (pos<0) throw BadArgument(tag()+" needs a comparision, not: "+args);
|
||||
cmd->_value1 = args.left(pos).trimmed();
|
||||
cmd->_cmp = args[pos].toLatin1();
|
||||
cmd->_value2 = args.mid(pos+1).trimmed();
|
||||
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
|
||||
QString comp("[=!.^~<>]");
|
||||
QStringList allargs = quotedStrings(args, comp, true);
|
||||
if (allargs.size()<2 || allargs[1].size()!=1 ||
|
||||
!QRegularExpression("^"+comp+"$").match(allargs[1]).hasMatch())
|
||||
throw BadArgument(tag()+" needs a comparision, not: "+args);
|
||||
if (allargs.size()>3)
|
||||
throw BadArgument(tag()+" has at most three arguments");
|
||||
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;
|
||||
}
|
||||
@@ -2704,6 +2779,49 @@ class Fail: public Command {
|
||||
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:
|
||||
class : public Command {
|
||||
@@ -2786,8 +2904,8 @@ inline bool Command::runScript(Logger& log, Command* parentCommand,
|
||||
scriptCopy.set(*var, parent->replacevars(*arg));
|
||||
}
|
||||
try {
|
||||
connect(&scriptCopy, SIGNAL(logging(QString)),
|
||||
parent, SLOT(parentlog(QString)));
|
||||
assert(connect(&scriptCopy, SIGNAL(logging(QString)),
|
||||
parent, SLOT(parentlog(QString))));
|
||||
parent->removeSignals(frame);
|
||||
bool res(scriptCopy.run(frame));
|
||||
parent->addSignals(frame);
|
||||
@@ -2845,6 +2963,7 @@ inline void Script::initPrototypes() {
|
||||
add(new Include);
|
||||
add(new Case);
|
||||
add(new Fail);
|
||||
add(new Auth);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -60,13 +60,13 @@ class NetworkAccessManager: public QNetworkAccessManager {
|
||||
return QNetworkAccessManager::configuration();
|
||||
}
|
||||
void connectToHost(const QString& hostName, quint16 port = 80) {
|
||||
//log(__PRETTY_FUNCTION__);
|
||||
//log(__PRETTY_FUNCTION__ + QString(" -> ") + hostName);
|
||||
QNetworkAccessManager::connectToHost(hostName, port);
|
||||
}
|
||||
void connectToHostEncrypted(const QString& hostName, quint16 port = 443,
|
||||
const QSslConfiguration& sslConfiguration
|
||||
= QSslConfiguration::defaultConfiguration()) {
|
||||
//log(__PRETTY_FUNCTION__);
|
||||
//log(__PRETTY_FUNCTION__ + QString(" -> ") + hostName);
|
||||
QNetworkAccessManager::connectToHostEncrypted(hostName, port,
|
||||
sslConfiguration);
|
||||
}
|
||||
@@ -171,7 +171,7 @@ class NetworkAccessManager: public QNetworkAccessManager {
|
||||
virtual QNetworkReply* createRequest(Operation op,
|
||||
const QNetworkRequest& req,
|
||||
QIODevice* outgoingData = 0) {
|
||||
//log(__PRETTY_FUNCTION__);
|
||||
//log(__PRETTY_FUNCTION__ + QString(" -> ") + req.url().url());
|
||||
switch (op) {
|
||||
case QNetworkAccessManager::HeadOperation: break;
|
||||
case QNetworkAccessManager::GetOperation: break;
|
||||
|
@@ -195,6 +195,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
//std::cout<<"titleChanged: "<<title.toStdString()<<std::endl;
|
||||
}
|
||||
void on__web_urlChanged(const QUrl& url) {
|
||||
_url->setText(url.url());
|
||||
enterText(true);
|
||||
if (_record->isChecked())
|
||||
appendCommand("expect "+map("urlChanged "+url.url()));
|
||||
|
@@ -115,6 +115,10 @@ int main(int argc, char *argv[]) try {
|
||||
(QStringList()<<"r"<<"retries",
|
||||
"on error retry up to <maxretries> times",
|
||||
"maxretries", "0"));
|
||||
parser.addOption(QCommandLineOption
|
||||
(QStringList()<<"timeout",
|
||||
"set default timeout in seconds",
|
||||
"timeout", "40"));
|
||||
parser.addOption(QCommandLineOption
|
||||
(QStringList()<<"W"<<"width",
|
||||
"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"));
|
||||
int retries(parser.value("retries").toInt());
|
||||
int timeout(parser.value("timeout").toInt());
|
||||
int width(parser.value("width").toInt());
|
||||
int height(parser.value("height").toInt());
|
||||
script.defaultTimeout(parser.value("timeout").toInt());
|
||||
QString target(parser.value("target-path"));
|
||||
p.resize(width, height);
|
||||
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuites"));
|
||||
|
Reference in New Issue
Block a user