|
|
|
@ -98,6 +98,10 @@ class Command: public QObject { |
|
|
|
|
virtual std::shared_ptr<Command> parse(Script*, QString, |
|
|
|
|
QStringList&, QString, int, int) = 0; |
|
|
|
|
virtual bool execute(Script*, QWebFrame*) = 0; |
|
|
|
|
static void error(Logger& log, const Exception& e) { |
|
|
|
|
log(QString(" FAILED: ")+e.what()); |
|
|
|
|
throw e; |
|
|
|
|
} |
|
|
|
|
void line(int linenr) { |
|
|
|
|
_line = linenr; |
|
|
|
|
} |
|
|
|
@ -128,9 +132,9 @@ class Command: public QObject { |
|
|
|
|
virtual bool isTestcase() { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
static void realMouseClick(QWebFrame* frame, QString selector) { |
|
|
|
|
static void realMouseClick(Logger& log, QWebFrame* frame, QString selector) { |
|
|
|
|
QWebElement element(find(frame, selector)); |
|
|
|
|
if (element.isNull()) throw ElementNotFound(selector); |
|
|
|
|
if (element.isNull()) error(log, ElementNotFound(selector)); |
|
|
|
|
realMouseClick(element); |
|
|
|
|
} |
|
|
|
|
static void realMouseClick(const QWebElement& element) { |
|
|
|
@ -168,7 +172,7 @@ class Command: public QObject { |
|
|
|
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 100); |
|
|
|
|
} |
|
|
|
|
protected: |
|
|
|
|
bool runScript(Command* parentCommand, |
|
|
|
|
bool runScript(Logger& log, Command* parentCommand, |
|
|
|
|
std::shared_ptr<Script> script, |
|
|
|
|
Script* parent, QWebFrame* frame, |
|
|
|
|
QStringList vars = QStringList(), |
|
|
|
@ -299,10 +303,10 @@ class Comment: public Command { |
|
|
|
|
|
|
|
|
|
class Screenshot: public Command { |
|
|
|
|
public: |
|
|
|
|
static QString sourceHtml(int line, QString target, QString base, |
|
|
|
|
static QString sourceHtml(Logger& log, int line, QString target, QString base, |
|
|
|
|
QString name, QWebFrame* frame) { |
|
|
|
|
if (!QDir(target).exists() && !QDir().mkpath(target)) |
|
|
|
|
throw DirectoryCannotBeCreated(target); |
|
|
|
|
error(log, DirectoryCannotBeCreated(target)); |
|
|
|
|
QCoreApplication::processEvents(); |
|
|
|
|
QString filename(target+QDir::separator()+ |
|
|
|
|
QString("%4-%1-%2-%3.html") |
|
|
|
@ -313,13 +317,13 @@ class Screenshot: public Command { |
|
|
|
|
.toString("yyyyMMddHHmmss"))); |
|
|
|
|
QFile file(filename); |
|
|
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) |
|
|
|
|
throw CannotWriteSouceHTML(filename); |
|
|
|
|
error(log, CannotWriteSouceHTML(filename)); |
|
|
|
|
QTextStream out(&file); |
|
|
|
|
out<<frame->toHtml(); |
|
|
|
|
if (out.status()!=QTextStream::Ok) throw CannotWriteSouceHTML(filename); |
|
|
|
|
if (out.status()!=QTextStream::Ok) error(log, CannotWriteSouceHTML(filename)); |
|
|
|
|
return QDir(filename).absolutePath(); |
|
|
|
|
} |
|
|
|
|
static QString screenshot(int line, QString target, QString base, |
|
|
|
|
static QString screenshot(Logger& log, int line, QString target, QString base, |
|
|
|
|
QString name, QWebFrame* frame) { |
|
|
|
|
bool wasShown(frame->page()->view()->isVisible()); |
|
|
|
|
frame->page()->view()->show(); |
|
|
|
@ -330,7 +334,7 @@ class Screenshot: public Command { |
|
|
|
|
painter.end(); |
|
|
|
|
if (!wasShown) frame->page()->view()->hide(); |
|
|
|
|
if (!QDir(target).exists() && !QDir().mkpath(target)) |
|
|
|
|
throw DirectoryCannotBeCreated(target); |
|
|
|
|
error(log, DirectoryCannotBeCreated(target)); |
|
|
|
|
QCoreApplication::processEvents(); |
|
|
|
|
QString filename(target+QDir::separator()+ |
|
|
|
|
QString("%4-%1-%2-%3.png") |
|
|
|
@ -339,7 +343,7 @@ class Screenshot: public Command { |
|
|
|
|
.arg(name) |
|
|
|
|
.arg(QDateTime::currentDateTime() |
|
|
|
|
.toString("yyyyMMddHHmmss"))); |
|
|
|
|
if (!image.save(filename)) throw CannotWriteScreenshot(filename); |
|
|
|
|
if (!image.save(filename)) error(log, CannotWriteScreenshot(filename)); |
|
|
|
|
return QDir(filename).absolutePath(); |
|
|
|
|
} |
|
|
|
|
QString tag() const { |
|
|
|
@ -639,8 +643,9 @@ class Script: public QObject { |
|
|
|
|
// timeout may happen during load due to bad internet connection
|
|
|
|
|
if (screenshots) |
|
|
|
|
try { // take a screenshot on error
|
|
|
|
|
Logger log(0, this); |
|
|
|
|
QString filename(Screenshot::screenshot |
|
|
|
|
((*cmd)->line(), targetdir(), |
|
|
|
|
(log, (*cmd)->line(), targetdir(), |
|
|
|
|
_testclass, |
|
|
|
|
QString("retry-%1") |
|
|
|
|
.arg((ulong)retries, 2, 10, |
|
|
|
@ -717,14 +722,16 @@ class Script: public QObject { |
|
|
|
|
if (screenshots) |
|
|
|
|
try { // write html source and take a last screenshot on error
|
|
|
|
|
{ |
|
|
|
|
Logger log(0, this); |
|
|
|
|
QString filename(Screenshot::sourceHtml |
|
|
|
|
((*cmd)->line(), targetdir(), |
|
|
|
|
(log, (*cmd)->line(), targetdir(), |
|
|
|
|
_testclass, |
|
|
|
|
"error", frame)); |
|
|
|
|
plainlog("[[ATTACHMENT|"+filename+"]]"); |
|
|
|
|
} { |
|
|
|
|
Logger log(0, this); |
|
|
|
|
QString filename(Screenshot::screenshot |
|
|
|
|
((*cmd)->line(), targetdir(), |
|
|
|
|
(log, (*cmd)->line(), targetdir(), |
|
|
|
|
_testclass, |
|
|
|
|
"error", frame)); |
|
|
|
|
plainlog("[[ATTACHMENT|"+filename+"]]"); |
|
|
|
@ -734,7 +741,7 @@ class Script: public QObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
removeSignals(frame); |
|
|
|
|
if (!_signals.empty()) throw UnhandledSignals(_signals); |
|
|
|
|
if (!_signals.empty()) error(UnhandledSignals(_signals)); |
|
|
|
|
progress("success", 0, 0); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
@ -798,9 +805,9 @@ class Script: public QObject { |
|
|
|
|
QStringList variables() { |
|
|
|
|
return _variables.keys(); |
|
|
|
|
} |
|
|
|
|
QString variable(QString name) { |
|
|
|
|
QString variable(Logger& log, QString name) { |
|
|
|
|
QMap<QString, QString>::iterator it(_variables.find(name)); |
|
|
|
|
if (it==_variables.end()) throw VariableNotFound(name); |
|
|
|
|
if (it==_variables.end()) error(VariableNotFound(name)); |
|
|
|
|
return *it; |
|
|
|
|
} |
|
|
|
|
/// Copy context from other script
|
|
|
|
@ -829,10 +836,10 @@ class Script: public QObject { |
|
|
|
|
void function(QString name, std::shared_ptr<Function> f) { |
|
|
|
|
_functions[name] = f; |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Function> function(QString name) { |
|
|
|
|
std::shared_ptr<Function> function(Logger& log, QString name) { |
|
|
|
|
QMap<QString, std::shared_ptr<Function> >::iterator |
|
|
|
|
it(_functions.find(name)); |
|
|
|
|
if (it==_functions.end()) throw FunctionNotFound(name); |
|
|
|
|
if (it==_functions.end()) error(FunctionNotFound(name)); |
|
|
|
|
return *it; |
|
|
|
|
} |
|
|
|
|
void timeout(int t) { |
|
|
|
@ -942,6 +949,10 @@ class Script: public QObject { |
|
|
|
|
_cout += text + "\n"; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
void error(const Exception& e) { |
|
|
|
|
log(QString(" FAILED: ")+e.what()); |
|
|
|
|
throw e; |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Command> unknown(QString command) { |
|
|
|
|
if (!command.size()) |
|
|
|
|
return std::shared_ptr<Command>(new Empty()); |
|
|
|
@ -998,7 +1009,7 @@ class Script: public QObject { |
|
|
|
|
QStringList(url.toString()))); |
|
|
|
|
} |
|
|
|
|
void timeout() { |
|
|
|
|
throw TimeOut(); |
|
|
|
|
error(TimeOut()); |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
typedef std::map<QString, std::shared_ptr<Command>> Prototypes; |
|
|
|
@ -1056,7 +1067,7 @@ class Do: public Command { |
|
|
|
|
if (_selector.size()) { |
|
|
|
|
element = find(frame, script->replacevars(_selector)); |
|
|
|
|
if (element.isNull()) |
|
|
|
|
throw ElementNotFound(script->replacevars(_selector)); |
|
|
|
|
error(log, ElementNotFound(script->replacevars(_selector))); |
|
|
|
|
} |
|
|
|
|
_result = |
|
|
|
|
element.evaluateJavaScript(script->replacevars(_javascript)).toString(); |
|
|
|
@ -1138,19 +1149,19 @@ class Expect: public Command { |
|
|
|
|
QStringList args(script->replacevars(_signal._args)); |
|
|
|
|
Script::Signal lastsignal(script->getSignal()); |
|
|
|
|
if (_signal._signal=="load") { // special signal load
|
|
|
|
|
if (lastsignal.first=="loadStarted") { |
|
|
|
|
log("ignore loadStarted"); |
|
|
|
|
while (lastsignal.first=="loadStarted") { |
|
|
|
|
log("ignore signal: loadStarted"); |
|
|
|
|
lastsignal = script->getSignal(); // ignore optional loadStarted
|
|
|
|
|
} |
|
|
|
|
if (lastsignal.first!="urlChanged" || (args.size() && lastsignal.second!=args)) |
|
|
|
|
throw WrongSignal("urlChanged", args, lastsignal); |
|
|
|
|
error(log, WrongSignal("urlChanged", args, lastsignal)); |
|
|
|
|
lastsignal = script->getSignal(); |
|
|
|
|
args=QStringList("true"); |
|
|
|
|
if (lastsignal.first!="loadFinished" || (lastsignal.second!=args)) |
|
|
|
|
throw WrongSignal("loadFinished", args, lastsignal); |
|
|
|
|
error(log, WrongSignal("loadFinished", args, lastsignal)); |
|
|
|
|
} else { |
|
|
|
|
if (lastsignal.first!=_signal._signal || (args.size() && lastsignal.second!=args)) |
|
|
|
|
throw WrongSignal(_signal._signal, args, lastsignal); |
|
|
|
|
error(log, WrongSignal(_signal._signal, args, lastsignal)); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -1221,8 +1232,8 @@ class Sleep: public Command { |
|
|
|
|
bool ok; |
|
|
|
|
unsigned int time(script->replacevars(_time).toUInt(&ok)); |
|
|
|
|
if (!ok) |
|
|
|
|
throw BadArgument(script->replacevars(_time) |
|
|
|
|
+" should be a number of seconds"); |
|
|
|
|
error(log, BadArgument(script->replacevars(_time) |
|
|
|
|
+" should be a number of seconds")); |
|
|
|
|
sleep(time); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -1342,7 +1353,7 @@ class Upload: public Command { |
|
|
|
|
QStringList allargs(args.split("->")); |
|
|
|
|
if (allargs.size()<2) |
|
|
|
|
throw BadArgument(tag()+"requires <selector> -> <filename>, " |
|
|
|
|
"instead of: \""+args+"\""); |
|
|
|
|
"instead of: \""+args+"\""); |
|
|
|
|
cmd->_selector = allargs.takeFirst().trimmed(); |
|
|
|
|
cmd->_filename = allargs.join("->").trimmed(); |
|
|
|
|
return cmd; |
|
|
|
@ -1364,9 +1375,9 @@ class Upload: public Command { |
|
|
|
|
if (files.size()==1) filename=files[0]; |
|
|
|
|
} |
|
|
|
|
page->setNextUploadFile(filename); |
|
|
|
|
realMouseClick(frame, script->replacevars(_selector)); |
|
|
|
|
realMouseClick(log, frame, script->replacevars(_selector)); |
|
|
|
|
if (page->uploadPrepared()) |
|
|
|
|
throw SetFileUploadFailed(script->replacevars(_selector), filename); |
|
|
|
|
error(log, SetFileUploadFailed(script->replacevars(_selector), filename)); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
@ -1416,14 +1427,14 @@ class Exists: public Command { |
|
|
|
|
} |
|
|
|
|
QWebElement element(find(frame, selector)); |
|
|
|
|
if (text.isEmpty()) |
|
|
|
|
throw AssertionFailed("element not found: "+selector); |
|
|
|
|
error(log, AssertionFailed("element not found: "+selector)); |
|
|
|
|
else if (element.isNull()) |
|
|
|
|
throw AssertionFailed("expected \""+text+"\" in non existing element " |
|
|
|
|
+selector); |
|
|
|
|
error(log, AssertionFailed("expected \""+text+"\" in non existing element " |
|
|
|
|
+selector)); |
|
|
|
|
else |
|
|
|
|
throw AssertionFailed("not found \""+text+"\" in \"" |
|
|
|
|
+notfound.join("\", \"")+"\" on "+selector); |
|
|
|
|
return true; // never reached due to throw above
|
|
|
|
|
error(log, AssertionFailed("not found \""+text+"\" in \"" |
|
|
|
|
+notfound.join("\", \"")+"\" on "+selector)); |
|
|
|
|
return true; // never reached due to error, above
|
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
QString _selector; |
|
|
|
@ -1465,10 +1476,10 @@ class Not: public Command { |
|
|
|
|
QString text(script->replacevars(_text)); |
|
|
|
|
Q_FOREACH(QWebElement element, frame->findAllElements(selector)) { |
|
|
|
|
if (text.isEmpty()) |
|
|
|
|
throw AssertionFailed("element must not exists: "+selector); |
|
|
|
|
error(log, AssertionFailed("element must not exists: "+selector)); |
|
|
|
|
if (element.toOuterXml().indexOf(text)!=-1) |
|
|
|
|
throw AssertionFailed("\""+text+"\" must not be in \"" |
|
|
|
|
+element.toInnerXml()+"\" on "+selector); |
|
|
|
|
error(log, AssertionFailed("\""+text+"\" must not be in \"" |
|
|
|
|
+element.toInnerXml()+"\" on "+selector)); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -1517,23 +1528,23 @@ class Execute: public Command { |
|
|
|
|
exec.setProcessChannelMode(QProcess::MergedChannels); |
|
|
|
|
exec.start(command, args); |
|
|
|
|
if (!exec.waitForStarted()) |
|
|
|
|
throw CannotStartScript(command, args, scripttxt); |
|
|
|
|
error(log, CannotStartScript(command, args, scripttxt)); |
|
|
|
|
if (scripttxt.size()) { |
|
|
|
|
if (exec.write(scripttxt.toUtf8())!=scripttxt.toUtf8().size() || |
|
|
|
|
!exec.waitForBytesWritten(60000)) |
|
|
|
|
throw CannotLoadScript(command, args, scripttxt); |
|
|
|
|
error(log, CannotLoadScript(command, args, scripttxt)); |
|
|
|
|
} |
|
|
|
|
exec.closeWriteChannel(); |
|
|
|
|
if (!exec.waitForFinished(60000) && exec.state()!=QProcess::NotRunning) |
|
|
|
|
throw ScriptNotFinished(command, args, scripttxt); |
|
|
|
|
error(log, ScriptNotFinished(command, args, scripttxt)); |
|
|
|
|
QString stdout(exec.readAllStandardOutput()); |
|
|
|
|
QString stderr(exec.readAllStandardError()); |
|
|
|
|
_result = stdout; |
|
|
|
|
log("result: "+(_result.size()?_result:"(void)")); |
|
|
|
|
script->log(stdout); |
|
|
|
|
if (exec.exitCode()!=0 || exec.exitStatus()!=QProcess::NormalExit) |
|
|
|
|
throw ScriptExecutionFailed(command, args, scripttxt, |
|
|
|
|
exec.exitCode(), stdout, stderr); |
|
|
|
|
error(log, ScriptExecutionFailed(command, args, scripttxt, |
|
|
|
|
exec.exitCode(), stdout, stderr)); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
@ -1585,8 +1596,8 @@ class Download: public Command { |
|
|
|
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 100); |
|
|
|
|
log("download terminated "+ |
|
|
|
|
QString(_netsuccess&&_filesuccess?"successfully":"with error")); |
|
|
|
|
if (!_netsuccess) throw DownloadFailed(_realfilename); |
|
|
|
|
if (!_filesuccess) throw WriteFileFailed(_realfilename); |
|
|
|
|
if (!_netsuccess) error(log, DownloadFailed(_realfilename)); |
|
|
|
|
if (!_filesuccess) error(log, WriteFileFailed(_realfilename)); |
|
|
|
|
log["[[ATTACHMENT|"+QDir(_realfilename).absolutePath()+"]]"]; |
|
|
|
|
disconnect(frame->page(), SIGNAL(unsupportedContent(QNetworkReply*)), |
|
|
|
|
this, SLOT(unsupportedContent(QNetworkReply*))); |
|
|
|
@ -1660,14 +1671,14 @@ class Click: public Command { |
|
|
|
|
QString clicktarget(script->replacevars(_selector)); |
|
|
|
|
switch (_clicktype ? *_clicktype : script->clicktype()) { |
|
|
|
|
case Script::REAL_MOUSE_CLICK: { |
|
|
|
|
realMouseClick(frame, clicktarget); |
|
|
|
|
realMouseClick(log, frame, clicktarget); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case Script::JAVASCRIPT_CLICK: |
|
|
|
|
default: { |
|
|
|
|
QWebElement element(find(frame, clicktarget)); |
|
|
|
|
if (element.isNull()) |
|
|
|
|
throw ElementNotFound(clicktarget); |
|
|
|
|
error(log, ElementNotFound(clicktarget)); |
|
|
|
|
_result = element.evaluateJavaScript("this.click();").toString(); |
|
|
|
|
if (_result.size()) log("result: "+_result.size()); |
|
|
|
|
break; |
|
|
|
@ -1791,8 +1802,8 @@ class Timeout: public Command { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
bool ok; |
|
|
|
|
int timeout(script->replacevars(_timeout).toInt(&ok)); |
|
|
|
|
if (!ok) throw BadArgument(script->replacevars(_timeout) |
|
|
|
|
+" should be a number of seconds"); |
|
|
|
|
if (!ok) error(log, BadArgument(script->replacevars(_timeout) |
|
|
|
|
+" should be a number of seconds")); |
|
|
|
|
script->timeout(timeout); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -1826,9 +1837,9 @@ class CaCertificate: public Command { |
|
|
|
|
QFile cacertfile(script->path()+filename); |
|
|
|
|
if (!cacertfile.exists()) cacertfile.setFileName(filename); // try without path
|
|
|
|
|
if (!cacertfile.exists() || !cacertfile.open(QIODevice::ReadOnly)) |
|
|
|
|
throw FileNotFound(filename); |
|
|
|
|
error(log, FileNotFound(filename)); |
|
|
|
|
QSslCertificate cacert(&cacertfile); |
|
|
|
|
if (cacert.isNull()) throw NotACertificate(filename); |
|
|
|
|
if (cacert.isNull()) error(log, NotACertificate(filename)); |
|
|
|
|
QSslSocket::addDefaultCaCertificate(cacert); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -1873,19 +1884,19 @@ class ClientCertificate: public Command { |
|
|
|
|
QFile certfile(script->path()+filename); |
|
|
|
|
if (!certfile.exists()) certfile.setFileName(filename); |
|
|
|
|
if (!certfile.exists() || !certfile.open(QIODevice::ReadOnly)) |
|
|
|
|
throw FileNotFound(filename); |
|
|
|
|
error(log, FileNotFound(filename)); |
|
|
|
|
QSslCertificate cert(&certfile); |
|
|
|
|
if (cert.isNull()) throw NotACertificate(filename); |
|
|
|
|
if (cert.isNull()) error(log, NotACertificate(filename)); |
|
|
|
|
sslConfig.setLocalCertificate(cert); |
|
|
|
|
filename = script->replacevars(_keyfile); |
|
|
|
|
QFile keyfile(script->path()+filename); |
|
|
|
|
if (!keyfile.exists()) keyfile.setFileName(filename); |
|
|
|
|
if (!keyfile.exists() || !keyfile.open(QIODevice::ReadOnly)) |
|
|
|
|
throw FileNotFound(filename); |
|
|
|
|
error(log, FileNotFound(filename)); |
|
|
|
|
keyfile.open(QIODevice::ReadOnly); |
|
|
|
|
QSslKey k(&keyfile, QSsl::Rsa, QSsl::Pem, |
|
|
|
|
QSsl::PrivateKey, script->replacevars(_password).toUtf8()); |
|
|
|
|
if (k.isNull()) throw KeyNotReadable(filename); |
|
|
|
|
if (k.isNull()) error(log, KeyNotReadable(filename)); |
|
|
|
|
sslConfig.setPrivateKey(k); |
|
|
|
|
QSslConfiguration::setDefaultConfiguration(sslConfig); |
|
|
|
|
return true; |
|
|
|
@ -1970,7 +1981,7 @@ class SetValue: public Command { |
|
|
|
|
QStringList allargs(args.split("->")); |
|
|
|
|
if (allargs.size()<2) |
|
|
|
|
throw BadArgument(tag()+" requires <selector> -> <value>, " |
|
|
|
|
"instead of: \""+args+"\""); |
|
|
|
|
"instead of: \""+args+"\""); |
|
|
|
|
cmd->_selector = allargs.takeFirst().trimmed(); |
|
|
|
|
cmd->_value = allargs.join("->").trimmed(); |
|
|
|
|
return cmd; |
|
|
|
@ -1979,7 +1990,7 @@ class SetValue: public Command { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
QWebElement element(find(frame, script->replacevars(_selector))); |
|
|
|
|
if (element.isNull()) |
|
|
|
|
throw ElementNotFound(script->replacevars(_selector)); |
|
|
|
|
error(log, ElementNotFound(script->replacevars(_selector))); |
|
|
|
|
QString value(script->replacevars(_value)); |
|
|
|
|
if (element.tagName()=="SELECT") { |
|
|
|
|
// value is a comma seperated list of option values
|
|
|
|
@ -2045,12 +2056,12 @@ class Function: public Command { |
|
|
|
|
Logger log(this, script, false); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
bool call(Command* parentCommand, QStringList args, |
|
|
|
|
Script* script, QWebFrame* frame) { |
|
|
|
|
bool call(Logger& log, Command* parentCommand, QStringList args, |
|
|
|
|
Script* script, QWebFrame* frame) { |
|
|
|
|
try { |
|
|
|
|
return runScript(parentCommand, _script, script, frame, _vars, args); |
|
|
|
|
} catch (const std::exception& x) { |
|
|
|
|
throw FunctionCallFailed(_name, _vars, args, x); |
|
|
|
|
return runScript(log, parentCommand, _script, script, frame, _vars, args); |
|
|
|
|
} catch (const Exception& x) { |
|
|
|
|
error(log, FunctionCallFailed(_name, _vars, args, x)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
@ -2087,8 +2098,8 @@ class Call: public Command { |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
return script->function(_name)->call(this, script->replacevars(_args), |
|
|
|
|
script, frame); |
|
|
|
|
return script->function(log, _name)->call(log, this, script->replacevars(_args), |
|
|
|
|
script, frame); |
|
|
|
|
} |
|
|
|
|
public: |
|
|
|
|
QString _name; |
|
|
|
@ -2178,28 +2189,28 @@ class If: public Command { |
|
|
|
|
+selector+" "+_cmp+" "+value); |
|
|
|
|
} else { |
|
|
|
|
switch (_cmp[0].toLatin1()) { |
|
|
|
|
case '=': check = script->variable(_variable)==value; |
|
|
|
|
case '=': check = script->variable(log, _variable)==value; |
|
|
|
|
break; |
|
|
|
|
case '!': check = script->variable(_variable)!=value; |
|
|
|
|
case '!': check = script->variable(log, _variable)!=value; |
|
|
|
|
break; |
|
|
|
|
case '.': check = script->variable(_variable).contains(value); |
|
|
|
|
case '.': check = script->variable(log, _variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '^': check = !script->variable(_variable).contains(value); |
|
|
|
|
case '^': check = !script->variable(log, _variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '~': check = |
|
|
|
|
script->variable(_variable).contains(QRegularExpression(value)); |
|
|
|
|
script->variable(log, _variable).contains(QRegularExpression(value)); |
|
|
|
|
break; |
|
|
|
|
case '<': check = script->variable(_variable).toInt()<value.toInt(); |
|
|
|
|
case '<': check = script->variable(log, _variable).toInt()<value.toInt(); |
|
|
|
|
break; |
|
|
|
|
case '>': check = script->variable(_variable).toInt()>value.toInt(); |
|
|
|
|
case '>': check = script->variable(log, _variable).toInt()>value.toInt(); |
|
|
|
|
break; |
|
|
|
|
default:; |
|
|
|
|
} |
|
|
|
|
log(QString("evaluated expression to ")+(check?"true":"false")+": " |
|
|
|
|
+script->variable(_variable)+" "+_cmp+" "+value); |
|
|
|
|
+script->variable(log, _variable)+" "+_cmp+" "+value); |
|
|
|
|
} |
|
|
|
|
if (check) return runScript(this, _script, script, frame); |
|
|
|
|
else if (_else) return runScript(this, _else, script, frame); |
|
|
|
|
if (check) return runScript(log, this, _script, script, frame); |
|
|
|
|
else if (_else) return runScript(log, this, _else, script, frame); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
@ -2332,7 +2343,7 @@ class Check: public Command { |
|
|
|
|
default:; |
|
|
|
|
} |
|
|
|
|
log("evaluated expression: "+value1+" "+_cmp+" "+value2); |
|
|
|
|
if (!check) throw CheckFailed(value1, _cmp, value2); |
|
|
|
|
if (!check) error(log, CheckFailed(value1, _cmp, value2)); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
@ -2379,8 +2390,8 @@ class For: public Command { |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
Q_FOREACH(QString i, _vals.size()?_vals:commaSeparatedList(script->variable(_variable))) { |
|
|
|
|
if (!runScript(this, _script, script, frame, QStringList()<<_variable, QStringList()<<i)) |
|
|
|
|
Q_FOREACH(QString i, _vals.size()?_vals:commaSeparatedList(script->variable(log, _variable))) { |
|
|
|
|
if (!runScript(log, this, _script, script, frame, QStringList()<<_variable, QStringList()<<i)) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
@ -2444,7 +2455,7 @@ class OfflineStoragePath: public Command { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
QDir path(_path); |
|
|
|
|
if (!path.exists() && !path.mkpath(_path)) |
|
|
|
|
throw DirectoryCannotBeCreated(_path); |
|
|
|
|
error(log, DirectoryCannotBeCreated(_path)); |
|
|
|
|
TestWebPage* page(dynamic_cast<TestWebPage*>(frame->page())); |
|
|
|
|
page->settings()->setOfflineStoragePath(_path); |
|
|
|
|
return true; |
|
|
|
@ -2513,16 +2524,18 @@ class Include: public Command { |
|
|
|
|
try { |
|
|
|
|
cmd->_script = std::shared_ptr<Script>(new Script); |
|
|
|
|
cmd->_script->parse(txt.split('\n'), cmd->_filename, 1, indent+1); |
|
|
|
|
} catch (std::exception& e) { |
|
|
|
|
} catch (Exception& e) { |
|
|
|
|
throw ParseIncludeFailed(cmd->_filename, e.what()); |
|
|
|
|
} |
|
|
|
|
return cmd; |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) try { |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
return runScript(this, _script, script, frame); |
|
|
|
|
} catch (std::exception& e) { |
|
|
|
|
throw ExecuteIncludeFailed(_filename, e.what()); |
|
|
|
|
try { |
|
|
|
|
return runScript(log, this, _script, script, frame); |
|
|
|
|
} catch (Exception& e) { |
|
|
|
|
error(log, ExecuteIncludeFailed(_filename, e.what())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
QString _filename; |
|
|
|
@ -2624,27 +2637,27 @@ class Case: public Command { |
|
|
|
|
+selector+" "+condition.cmp+" "+value); |
|
|
|
|
} else { |
|
|
|
|
switch (condition.cmp[0].toLatin1()) { |
|
|
|
|
case '=': check = script->variable(_variable)==value; |
|
|
|
|
case '=': check = script->variable(log, _variable)==value; |
|
|
|
|
break; |
|
|
|
|
case '!': check = script->variable(_variable)!=value; |
|
|
|
|
case '!': check = script->variable(log, _variable)!=value; |
|
|
|
|
break; |
|
|
|
|
case '.': check = script->variable(_variable).contains(value); |
|
|
|
|
case '.': check = script->variable(log, _variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '^': check = !script->variable(_variable).contains(value); |
|
|
|
|
case '^': check = !script->variable(log, _variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '~': check = |
|
|
|
|
script->variable(_variable).contains(QRegularExpression(value)); |
|
|
|
|
script->variable(log, _variable).contains(QRegularExpression(value)); |
|
|
|
|
break; |
|
|
|
|
case '<': check = script->variable(_variable).toInt()<value.toInt(); |
|
|
|
|
case '<': check = script->variable(log, _variable).toInt()<value.toInt(); |
|
|
|
|
break; |
|
|
|
|
case '>': check = script->variable(_variable).toInt()>value.toInt(); |
|
|
|
|
case '>': check = script->variable(log, _variable).toInt()>value.toInt(); |
|
|
|
|
break; |
|
|
|
|
default:; |
|
|
|
|
} |
|
|
|
|
log(QString("evaluated expression to ")+(check?"true":"false")+": " |
|
|
|
|
+script->variable(_variable)+" "+condition.cmp+" "+value); |
|
|
|
|
+script->variable(log, _variable)+" "+condition.cmp+" "+value); |
|
|
|
|
} |
|
|
|
|
if (check) return runScript(this, condition.script, script, frame); |
|
|
|
|
if (check) return runScript(log, this, condition.script, script, frame); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
@ -2684,7 +2697,7 @@ class Fail: public Command { |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame*) { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
throw TestFailed(script->replacevars(_text)); |
|
|
|
|
error(log, TestFailed(script->replacevars(_text))); |
|
|
|
|
return true; // dummy
|
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
@ -2725,7 +2738,7 @@ inline bool Screenshot::execute(Script* script, QWebFrame* frame) { |
|
|
|
|
log("screenshots disabled"); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
QString filename(screenshot(line(), script->targetdir(), |
|
|
|
|
QString filename(screenshot(log, line(), script->targetdir(), |
|
|
|
|
script->testclass(), |
|
|
|
|
script->replacevars(_filename), frame)); |
|
|
|
|
log["[[ATTACHMENT|"+filename+"]]"]; |
|
|
|
@ -2734,26 +2747,30 @@ inline bool Screenshot::execute(Script* script, QWebFrame* frame) { |
|
|
|
|
|
|
|
|
|
inline Logger::Logger(Command* command, Script* script, bool showLines): |
|
|
|
|
_command(command), _script(script) { |
|
|
|
|
_previous = _script->command(); |
|
|
|
|
_script->command(command); |
|
|
|
|
if (_command->log()) |
|
|
|
|
if (showLines) |
|
|
|
|
_script->log("\\ "+_command->command(), _command); |
|
|
|
|
else |
|
|
|
|
_script->log("\\ "+_command->command().split('\n').first(), _command); |
|
|
|
|
if (command) { |
|
|
|
|
_previous = _script->command(); |
|
|
|
|
_script->command(command); |
|
|
|
|
if (_command->log()) |
|
|
|
|
if (showLines) |
|
|
|
|
_script->log("\\ "+_command->command(), _command); |
|
|
|
|
else |
|
|
|
|
_script->log("\\ "+_command->command().split('\n').first(), _command); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
inline void Logger::operator()(QString txt) { |
|
|
|
|
if (_command->log()) _script->log(" "+txt, _command); |
|
|
|
|
if (!_command || _command->log()) _script->log(" "+txt, _command); |
|
|
|
|
} |
|
|
|
|
inline void Logger::operator[](QString txt) { |
|
|
|
|
_script->plainlog(txt); |
|
|
|
|
} |
|
|
|
|
inline Logger::~Logger() { |
|
|
|
|
if (_command->log()) _script->log("/ "+_command->tag(), _command); |
|
|
|
|
_script->command(_previous); |
|
|
|
|
if (_command) { |
|
|
|
|
if (_command->log()) _script->log("/ "+_command->tag(), _command); |
|
|
|
|
_script->command(_previous); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline bool Command::runScript(Command* parentCommand, |
|
|
|
|
inline bool Command::runScript(Logger& log, Command* parentCommand, |
|
|
|
|
std::shared_ptr<Script> script, |
|
|
|
|
Script* parent, QWebFrame* frame, |
|
|
|
|
QStringList vars, |
|
|
|
@ -2761,7 +2778,7 @@ inline bool Command::runScript(Command* parentCommand, |
|
|
|
|
Script scriptCopy(*script); // only work with a copy of script
|
|
|
|
|
scriptCopy.set(*parent); |
|
|
|
|
if (args.size()!=vars.size()) |
|
|
|
|
throw WrongNumberOfArguments(vars, args); |
|
|
|
|
error(log, WrongNumberOfArguments(vars, args)); |
|
|
|
|
for (QStringList::iterator var(vars.begin()), arg(args.begin()); |
|
|
|
|
var<vars.end() && arg<args.end(); ++var, ++arg) { |
|
|
|
|
parent->log("argument: "+*var+" = "+parent->replacevars(*arg), |
|
|
|
@ -2778,13 +2795,13 @@ inline bool Command::runScript(Command* parentCommand, |
|
|
|
|
parent, SLOT(parentlog(QString))); |
|
|
|
|
parentCommand->_result = scriptCopy.result(); |
|
|
|
|
Q_FOREACH(QString key, scriptCopy.variables()) // copy new variables to parent
|
|
|
|
|
if (!vars.contains(key)) parent->set(key, scriptCopy.variable(key)); |
|
|
|
|
if (!vars.contains(key)) parent->set(key, scriptCopy.variable(log, key)); |
|
|
|
|
Q_FOREACH(QString key, scriptCopy.functions()) // copy new functions to parent
|
|
|
|
|
parent->function(key, scriptCopy.function(key)); |
|
|
|
|
parent->function(key, scriptCopy.function(log, key)); |
|
|
|
|
if (parentCommand->_result.size()) |
|
|
|
|
parent->log("result: "+parentCommand->_result); |
|
|
|
|
return res; |
|
|
|
|
} catch (const std::exception& x) { |
|
|
|
|
} catch (const Exception& x) { |
|
|
|
|
parent->addSignals(frame); |
|
|
|
|
disconnect(&scriptCopy, SIGNAL(logging(QString)), |
|
|
|
|
parent, SLOT(parentlog(QString))); |
|
|
|
|