@ -21,6 +21,7 @@
# include <QTimer>
# include <QProcess>
# include <QMouseEvent>
# include <QRegularExpression>
# include <vector>
# include <queue>
# include <map>
@ -35,8 +36,8 @@ class Command;
class Logger {
public :
Logger ( Command * command , Script * script ) ;
void plainlog ( QString txt ) ;
void log ( QString txt ) ;
void operator [ ] ( QString txt ) ;
void operator ( ) ( QString txt ) ;
~ Logger ( ) ;
private :
Command * _command ;
@ -170,7 +171,7 @@ class Empty: public Command {
" Empty lines are allowed " ;
}
QString command ( ) const {
return " " ;
return tag ( ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString , QStringList & , int ) {
std : : shared_ptr < Empty > cmd ( new Empty ( ) ) ;
@ -258,14 +259,14 @@ class Screenshot: public Command {
}
QString description ( ) const {
return
" screenshot <filename-base>"
tag ( ) + " <filename-base> "
" \n \n "
" Create a PNG screenshot of the actual web page and store it in the "
" file <filename-base>.png. If not already opened, a browser window "
" will pop up to take the screenshot. " ;
}
QString command ( ) const {
return " screenshot " + _filename ;
return tag ( ) + " " + _filename ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Screenshot > cmd ( new Screenshot ( ) ) ;
@ -325,6 +326,25 @@ class Script: public QObject {
void logging ( QString ) ;
public :
typedef std : : pair < QString , QStringList > Signal ;
enum ClickType {
REAL_MOUSE_CLICK ,
JAVASCRIPT_CLICK
} ;
enum Formatting {
PLAIN ,
HTML
} ;
/// QString that sorts first by string length, then by content
class LenString : public QString {
public :
LenString ( ) { }
LenString ( const QString & o ) : QString ( o ) { }
virtual bool operator < ( const LenString & o ) const {
return
size ( ) < o . size ( ) ? true : o . size ( ) < size ( ) ? false
: QString : : operator < ( o . toLatin1 ( ) ) ;
}
} ;
public :
static QString xmlattr ( QString attr , bool br = false ) {
attr . replace ( " & " , " & " ) //.replace(" ", " ")
@ -343,7 +363,7 @@ class Script: public QObject {
. replace ( " " , " " ) . replace ( " & " , " & " ) ;
}
public :
Script ( ) {
Script ( ) : _clicktype ( JAVASCRIPT_CLICK ) {
initPrototypes ( ) ;
}
QString syntax ( ) const {
@ -355,10 +375,34 @@ class Script: public QObject {
" Note: When a selector is required as parameter, then the selector "
" is a CSS selector that must not contain spaces. " ;
}
QString commands ( ) const {
QString commands ( Formatting f = PLAIN ) const {
QString cmds ;
for ( auto it ( _prototypes . begin ( ) ) ; it ! = _prototypes . end ( ) ; + + it )
cmds + = " \n \n " + it - > second - > description ( ) ;
switch ( f ) {
case PLAIN : {
cmds + = " \n \n \n COMMAND: " + it - > first + " \n \n " + it - > second - > description ( ) ;
} break ;
case HTML : {
cmds + = " <h1> " + it - > first + " </h1><p> "
+ it - > second - > description ( )
. replace ( " & " , " & " )
. replace ( " < " , " < " )
. replace ( " > " , " > " )
. replace ( QRegularExpression ( " <([^ ]+)> " ) ,
" <i> \\ 1</i> " )
. replace ( QRegularExpression ( " ( \n [^ ][^ \n ]*)( \n +-) " ) ,
" \\ 1<ul> \\ 2 " )
. replace ( QRegularExpression ( " ( \n +-[^ \n ]* \n )([^ ]) " ) ,
" \\ 1</ul> \\ 2 " )
. replace ( QRegularExpression ( " \n +- ([^ \n ]*)(</ul>)? " ) ,
" <li> \\ 1</li> \\ 2 " )
. replace ( " </li> \n " , " </li> " )
. replace ( " \n " , " \n " )
. replace ( " \n \n " , " </p><p> " )
. replace ( " \n " , " <br/> " )
+ " </p> " ;
} break ;
}
return cmds . trimmed ( ) ;
}
void reset ( ) {
@ -368,6 +412,14 @@ class Script: public QObject {
_ignores . clear ( ) ;
_cout . clear ( ) ;
_cerr . clear ( ) ;
_ignoreSignalsUntil . clear ( ) ;
}
void cleanup ( ) {
reset ( ) ;
_variables . clear ( ) ;
_rvariables . clear ( ) ;
_timeout = 20 ;
_clicktype = JAVASCRIPT_CLICK ;
}
std : : shared_ptr < Command > parse ( QStringList & in , int linenr ) try {
QString line ( in . takeFirst ( ) . trimmed ( ) ) ;
@ -555,19 +607,36 @@ class Script: public QObject {
}
void set ( QString name , QString value ) {
_variables [ name ] = value ;
_rvariables [ value ] = name ;
}
void unset ( QString name ) {
_rvariables . remove ( _variables [ name ] ) ;
_variables . remove ( name ) ;
}
void timeout ( int t ) {
_timeout = t ;
}
void clicktype ( ClickType c ) {
_clicktype = c ;
}
ClickType clicktype ( ) {
return _clicktype ;
}
QString replacevars ( QString txt ) {
for ( QMap < QString , QString > : : iterator it ( _variables . begin ( ) ) ;
it ! = _variables . end ( ) ; + + it )
txt . replace ( it . key ( ) , it . value ( ) ) ;
return txt ;
}
QString insertvars ( QString txt ) {
QMapIterator < LenString , LenString > it ( _rvariables ) ;
it . toBack ( ) ;
while ( it . hasPrevious ( ) ) {
it . previous ( ) ;
txt . replace ( it . key ( ) , it . value ( ) ) ;
}
return txt ;
}
public Q_SLOTS :
void log ( QString text ) {
text = QDateTime : : currentDateTime ( ) . toString ( " yyyy-MM-dd HH:mm:ss " ) + text ;
@ -703,8 +772,10 @@ class Script: public QObject {
QString _cerr ;
bool _screenshots ;
QString _ignoreSignalsUntil ;
QMap < QString , QString > _variables ;
QMap < QString , QString > _variables ; ///< variable mapping
QMap < LenString , LenString > _rvariables ; ///< reverse variable mapping
int _timeout ;
ClickType _clicktype ;
} ;
class Do : public Command {
@ -714,7 +785,7 @@ class Do: public Command {
}
QString description ( ) const {
return
" do <selector>\n <javascript-line1> \n <javascript-line2> "
tag ( ) + " <selector> \n <javascript-line1> \n <javascript-line2> "
" \n \n "
" Execute JavaScript on a CSS selected object. The object is the first "
" object in the DOM tree that matches the given CSS selector. You can "
@ -723,7 +794,7 @@ class Do: public Command {
" one space " ;
}
QString command ( ) const {
return " do " + _selector + _javascript ;
return tag ( ) + " " + _selector + _javascript ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & in , int ) {
@ -735,8 +806,9 @@ class Do: public Command {
}
bool execute ( Script * script , QWebFrame * frame ) {
Logger log ( this , script ) ;
QWebElement element ( find ( frame , _selector ) ) ;
if ( element . isNull ( ) ) throw ElementNotFound ( _selector ) ;
QWebElement element ( find ( frame , script - > replacevars ( _selector ) ) ) ;
if ( element . isNull ( ) )
throw ElementNotFound ( script - > replacevars ( _selector ) ) ;
_result =
element . evaluateJavaScript ( script - > replacevars ( _javascript ) ) . toString ( ) ;
return true ;
@ -753,12 +825,12 @@ class Load: public Command {
}
QString description ( ) const {
return
" load <url>"
tag ( ) + " <url> "
" \n \n "
" Load an URL, the URL is given as parameter in full syntax. " ;
}
QString command ( ) const {
return " load " + _url ;
return tag ( ) + " " + _url ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Load > cmd ( new Load ( ) ) ;
@ -781,7 +853,7 @@ class Expect: public Command {
}
QString description ( ) const {
return
" expect <signal> [<parameter>]"
tag ( ) + " <signal> [<parameter>] "
" \n \n "
" Expect a signal. Signals are emitted by webkit and may contain "
" parameter. If a parameter is given in the script, then the parameter "
@ -800,7 +872,7 @@ class Expect: public Command {
" - loadFinished true " ;
}
QString command ( ) const {
return " expect " + _signal . _signal
return tag ( ) + " " + _signal . _signal
+ ( _signal . _args . size ( ) ? " " + _signal . _args . join ( ' ' ) : QString ( ) ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
@ -851,12 +923,12 @@ class Open: public Command {
}
QString description ( ) const {
return
" open "
tag ( ) +
" \n \n "
" Open the browser window, so you can follow the test steps visually. " ;
}
QString command ( ) const {
return " open " ;
return tag ( ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString , QStringList & , int ) {
std : : shared_ptr < Open > cmd ( new Open ( ) ) ;
@ -876,14 +948,14 @@ class Sleep: public Command {
}
QString description ( ) const {
return
" sleep <seconds>"
tag ( ) + " <seconds> "
" \n \n "
" Sleep for a certain amount of seconds. This helps, if you must wait "
" for some javascript actions, i.e. AJAX or slow pages, and the "
" excpeted signals are not sufficient. " ;
}
QString command ( ) const {
return " sleep " + _time ;
return tag ( ) + " " + _time ;
}
std : : shared_ptr < Command > parse ( Script * , QString time , QStringList & , int ) {
std : : shared_ptr < Sleep > cmd ( new Sleep ( ) ) ;
@ -913,14 +985,14 @@ class Exit: public Command {
}
QString description ( ) const {
return
" exit "
tag ( ) +
" \n \n "
" Successfully terminate script immediately. The following commands "
" are not executed. This helps when you debug your scripts and you "
" want the script stop at a certain point for investigations. " ;
}
QString command ( ) const {
return " exit " ;
return tag ( ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString , QStringList & , int ) {
std : : shared_ptr < Exit > cmd ( new Exit ( ) ) ;
@ -939,7 +1011,7 @@ class IgnoreTo: public Command {
}
QString description ( ) const {
return
" ignoreto <label>"
tag ( ) + " <label> "
" \n \n "
" Ignore all following commands up to a given label. The following "
" commands are not executed until the given label appears in the "
@ -947,7 +1019,7 @@ class IgnoreTo: public Command {
" want to skip some lines of script code. " ;
}
QString command ( ) const {
return " ignoreto " + _label ;
return tag ( ) + " " + _label ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < IgnoreTo > cmd ( new IgnoreTo ( ) ) ;
@ -971,12 +1043,12 @@ class Label: public Command {
}
QString description ( ) const {
return
" label <label>"
tag ( ) + " <label> "
" \n \n "
" This marks the label refered by command \" ignoreto \" . " ;
}
QString command ( ) const {
return " label " + _label ;
return tag ( ) + " " + _label ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Label > cmd ( new Label ( ) ) ;
@ -1000,23 +1072,23 @@ class Upload: public Command {
}
QString description ( ) const {
return
" upload <selector> -> <filename>"
tag ( ) + " <selector> -> <filename> "
" \n \n "
" Presses the specified file upload button and passes a given file "
" name. The command requires a CSS selector followed by a filename. "
" The first object that matches the selector is used. " ;
}
QString command ( ) const {
return " upload " + _selector + " -> " + _filename ;
return tag ( ) + " " + _selector + " -> " + _filename ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Upload > cmd ( new Upload ( ) ) ;
QStringList allargs ( args . split ( " -> " ) ) ;
if ( allargs . size ( ) < 2 )
throw BadArgument ( " upload needs a selector folowed by a filename , "
throw BadArgument ( tag ( ) + " requires <selector> -> <filename> , "
" instead of: \" " + args + " \" " ) ;
cmd - > _selector = allargs . takeFirst ( ) . trimmed ( ) ;
cmd - > _filename = allargs . join ( " " ) . trimmed ( ) ;
cmd - > _filename = allargs . join ( " -> " ) . trimmed ( ) ;
return cmd ;
}
bool execute ( Script * script , QWebFrame * frame ) {
@ -1047,7 +1119,7 @@ class Exists: public Command {
}
QString description ( ) const {
return
" exists <selector> -> <text>"
tag ( ) + " <selector> -> <text> "
" \n \n "
" Assert that a certain text exists in the selected object, or if no "
" text is given, assert that the specified object exists. The object "
@ -1055,7 +1127,7 @@ class Exists: public Command {
" text. " ;
}
QString command ( ) const {
return " exists " + _selector + ( _text . size ( ) ? " -> " + _text : QString ( ) ) ;
return tag ( ) + " " + _selector + ( _text . size ( ) ? " -> " + _text : QString ( ) ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Exists > cmd ( new Exists ( ) ) ;
@ -1064,7 +1136,7 @@ class Exists: public Command {
cmd - > _selector = args ;
} else {
cmd - > _selector = allargs . takeFirst ( ) . trimmed ( ) ;
cmd - > _text = allargs . join ( " " ) . trimmed ( ) ;
cmd - > _text = allargs . join ( " -> " ) . trimmed ( ) ;
}
return cmd ;
}
@ -1099,7 +1171,7 @@ class Not: public Command {
}
QString description ( ) const {
return
" not <selector> -> <text>"
tag ( ) + " <selector> -> <text> "
" \n \n "
" Assert that a certain text does not exists in the selected object, "
" or if no text is given, assert that the specified object does not "
@ -1107,7 +1179,7 @@ class Not: public Command {
" are search for the text. " ;
}
QString command ( ) const {
return " not " + _selector + ( _text . size ( ) ? " -> " + _text : QString ( ) ) ;
return tag ( ) + " " + _selector + ( _text . size ( ) ? " -> " + _text : QString ( ) ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Not > cmd ( new Not ( ) ) ;
@ -1116,7 +1188,7 @@ class Not: public Command {
cmd - > _selector = args ;
} else {
cmd - > _selector = allargs . takeFirst ( ) . trimmed ( ) ;
cmd - > _text = allargs . join ( " " ) . trimmed ( ) ;
cmd - > _text = allargs . join ( " -> " ) . trimmed ( ) ;
}
return cmd ;
}
@ -1145,7 +1217,7 @@ class Execute: public Command {
}
QString description ( ) const {
return
" execute <command>\n <line1> \n <line2> \n <...> "
tag ( ) + " <command> \n <line1> \n <line2> \n <...> "
" \n \n "
" Execute <command>. The command can have space separated arguments. "
" Following lines that are intended by at least "
@ -1154,8 +1226,8 @@ class Execute: public Command {
}
QString command ( ) const {
QStringList script ( _script ) ;
script . replaceInStrings ( QRegExp ( " ^ " ) , " " ) ;
return " execute " + _command
script . replaceInStrings ( QRegular Expression ( " ^ " ) , " " ) ;
return tag ( ) + " " + _command
+ ( _args . size ( ) ? " " + _args . join ( ' ' ) : QString ( ) )
+ ( script . size ( ) ? " \n " + script . join ( " \n " ) : QString ( ) ) ;
}
@ -1214,15 +1286,16 @@ class Download: public Command {
}
QString description ( ) const {
return
" download <filename>"
tag ( ) + " <filename> "
" <command-to-start-download> "
" \n \n "
" Set download file before loading a download link or clicking on a "
" download button. The next line must be exactly one command that "
" initiates the download. " ;
" initiates the download. \n \n "
" Please note that variables are not substituted in the filename. " ;
}
QString command ( ) const {
return " download " + ( _filename . size ( ) ? " " + _filename : QString ( ) ) + " \n "
return tag ( ) + ( _filename . size ( ) ? " " + _filename : QString ( ) ) + " \n "
+ _next - > command ( ) ;
}
std : : shared_ptr < Command > parse ( Script * script , QString args ,
@ -1243,11 +1316,11 @@ class Download: public Command {
script - > timer ( ) . stop ( ) ; // no timeout during download
for ( _done = false ; ! _done ; ) // wait for download finish
QCoreApplication : : processEvents ( QEventLoop : : AllEvents , 100 ) ;
log . log ( " download terminated " +
QString ( _netsuccess & & _filesuccess ? " successfully " : " with error " ) ) ;
log ( " download terminated " +
QString ( _netsuccess & & _filesuccess ? " successfully " : " with error " ) ) ;
if ( ! _netsuccess ) throw DownloadFailed ( _realfilename ) ;
if ( ! _filesuccess ) throw WriteFileFailed ( _realfilename ) ;
log . plainlog ( " [[ATTACHMENT| " + QDir ( _realfilename ) . absolutePath ( ) + " ]] " ) ;
log [ " [[ATTACHMENT| " + QDir ( _realfilename ) . absolutePath ( ) + " ]] " ] ;
disconnect ( frame - > page ( ) , SIGNAL ( unsupportedContent ( QNetworkReply * ) ) ,
this , SLOT ( unsupportedContent ( QNetworkReply * ) ) ) ;
return res ;
@ -1271,8 +1344,8 @@ class Download: public Command {
. isValid ( ) ) {
QString part ( reply - > header ( QNetworkRequest : : ContentDispositionHeader )
. toString ( ) ) ;
if ( part . contains ( QRegExp ( " attachment; *filename= " ) ) ) {
part . replace ( QRegExp ( " .*attachment; *filename= " ) , " " ) ;
if ( part . contains ( QRegular Expression ( " attachment; *filename= " ) ) ) {
part . replace ( QRegular Expression ( " .*attachment; *filename= " ) , " " ) ;
if ( part . size ( ) ) _realfilename = part ;
}
}
@ -1293,12 +1366,12 @@ class Click: public Command {
}
QString description ( ) const {
return
" click <selector>"
tag ( ) + " <selector> "
" \n \n "
" Click on the specified element " ;
}
QString command ( ) const {
return " click " + _selector ;
return tag ( ) + " " + _selector ;
}
std : : shared_ptr < Command > parse ( Script * , QString args , QStringList & , int ) {
std : : shared_ptr < Click > cmd ( new Click ( ) ) ;
@ -1307,7 +1380,22 @@ class Click: public Command {
}
bool execute ( Script * script , QWebFrame * frame ) {
Logger log ( this , script ) ;
realMouseClick ( frame , script - > replacevars ( _selector ) ) ;
switch ( script - > clicktype ( ) ) {
case Script : : REAL_MOUSE_CLICK : {
log ( " Script::REAL_MOUSE_CLICK " ) ;
realMouseClick ( frame , script - > replacevars ( _selector ) ) ;
break ;
}
case Script : : JAVASCRIPT_CLICK :
default : {
log ( " Script::JAVASCRIPT_CLICK " ) ;
QWebElement element ( find ( frame , script - > replacevars ( _selector ) ) ) ;
if ( element . isNull ( ) )
throw ElementNotFound ( script - > replacevars ( _selector ) ) ;
_result = element . evaluateJavaScript ( " this.click(); " ) . toString ( ) ;
break ;
}
}
return true ;
}
private :
@ -1321,8 +1409,8 @@ class Set: public Command {
}
QString description ( ) const {
return
" set <variable>=<value>\n "
" set <variable>\n "
tag ( ) + " <variable>=<value> \n " +
tag ( ) + " <variable> \n "
" <command> "
" \n \n "
" Sets the value of a variable either to a constant, or to the output "
@ -1332,9 +1420,9 @@ class Set: public Command {
}
QString command ( ) const {
if ( _next )
return " set " + _name + " \n " + _next - > command ( ) ;
return tag ( ) + " " + _name + " \n " + _next - > command ( ) ;
else
return " set " + _name + " = " + _value ;
return tag ( ) + " " + _name + " = " + _value ;
}
std : : shared_ptr < Command > parse ( Script * script , QString args ,
QStringList & in , int line ) {
@ -1374,12 +1462,12 @@ class UnSet: public Command {
}
QString description ( ) const {
return
" unset <variable>"
tag ( ) + " <variable> "
" \n \n "
" Undo the setting of a variable. The opposite of «set». " ;
}
QString command ( ) const {
return " unset " + _name ;
return tag ( ) + " " + _name ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & , int ) {
@ -1403,12 +1491,12 @@ class Timeout: public Command {
}
QString description ( ) const {
return
" timeout <seconds>"
tag ( ) + " <seconds> "
" \n \n "
" Set the timeout in seconds (defaults to 10). " ;
}
QString command ( ) const {
return " timeout " + _timeout ;
return tag ( ) + " " + _timeout ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & , int ) {
@ -1436,12 +1524,12 @@ class CaCertificate: public Command {
}
QString description ( ) const {
return
" ca-certificate <filename.pem>"
tag ( ) + " <filename.pem> "
" \n \n "
" Load a CA certificate that will be accepted on SSL connections. " ;
}
QString command ( ) const {
return " ca-certificate " + _filename ;
return tag ( ) + " " + _filename ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & , int ) {
@ -1471,7 +1559,7 @@ class ClientCertificate: public Command {
}
QString description ( ) const {
return
" client-certificate <certfile.pem> <keyfile.key> <keypassword>"
tag ( ) + " <certfile.pem> <keyfile.key> <keypassword> "
" \n \n "
" Load a client certificate to authenticate on SSL connections. "
" The password for the keyfile should not contain spaces. "
@ -1480,7 +1568,7 @@ class ClientCertificate: public Command {
" openssl pkcs12 -in certfile.p12 -out keyfile.pem -nocerts \n " ;
}
QString command ( ) const {
return " client-certificate " + _certfile + " " + _keyfile + " " + _password ;
return tag ( ) + " " + _certfile + " " + _keyfile + " " + _password ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & , int ) {
@ -1510,7 +1598,7 @@ class ClientCertificate: public Command {
throw FileNotFound ( filename ) ;
keyfile . open ( QIODevice : : ReadOnly ) ;
QSslKey k ( & keyfile , QSsl : : Rsa , QSsl : : Pem ,
QSsl : : PrivateKey , _password . toUtf8 ( ) ) ;
QSsl : : PrivateKey , script - > replacevars ( _password ) . toUtf8 ( ) ) ;
if ( k . isNull ( ) ) throw KeyNotReadable ( filename ) ;
sslConfig . setPrivateKey ( k ) ;
QSslConfiguration : : setDefaultConfiguration ( sslConfig ) ;
@ -1522,6 +1610,126 @@ class ClientCertificate: public Command {
QString _password ;
} ;
class ClickType : public Command {
public :
QString tag ( ) const {
return " clicktype " ;
}
QString description ( ) const {
return
tag ( ) + " <type> "
" \n \n "
" Set how mouseclicks should be mapped. The best solution depends on "
" your problem. Normally it is good to call \" click() \" on the element "
" using javascript. But with complex javascript infected websites, it "
" might be necessary to simulate a real mouse click. \n \n "
" <type> is one of: \n "
" - realmouse \n "
" - javascript (default) " ;
}
QString command ( ) const {
switch ( _clicktype ) {
case Script : : REAL_MOUSE_CLICK : return tag ( ) + " realmouse " ;
case Script : : JAVASCRIPT_CLICK : default :
return tag ( ) + " javascript " ;
}
}
std : : shared_ptr < Command > parse ( Script * script , QString args ,
QStringList & , int ) {
std : : shared_ptr < ClickType > cmd ( new ClickType ( ) ) ;
QString choice ( script - > replacevars ( args ) . trimmed ( ) ) ;
if ( choice = = " realmouse " )
cmd - > _clicktype = Script : : REAL_MOUSE_CLICK ;
else if ( choice = = " javascript " )
cmd - > _clicktype = Script : : JAVASCRIPT_CLICK ;
else
throw BadArgument ( " unknown clicktype: " + choice ) ;
return cmd ;
}
bool execute ( Script * script , QWebFrame * ) {
Logger log ( this , script ) ;
script - > clicktype ( _clicktype ) ;
return true ;
}
private :
Script : : ClickType _clicktype ;
} ;
class SetValue : public Command {
public :
QString tag ( ) const {
return " setvalue " ;
}
QString description ( ) const {
return
tag ( ) + " <selector> -> '<value>' \n " +
tag ( ) + " <selector> -> '<value1>', '<value2>', ... "
" \n \n "
" Set the selected element to a given value. It is mostly the same as "
" the following constuct, except that options in a select are evaluated "
" correctly: \n \n "
" do <selector> \n "
" this.value='<value>'; \n \n "
" The second syntax with comma separated list of valus applies for "
" options in a select element \n \n "
" If you quote the values, then quote all values with the same "
" quotes. If you need a comma within a value, you must quote. " ;
}
QString command ( ) const {
return tag ( ) + " " + _selector + " -> " + _value ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & , int ) {
std : : shared_ptr < SetValue > cmd ( new SetValue ( ) ) ;
QStringList allargs ( args . split ( " -> " ) ) ;
if ( allargs . size ( ) < 2 )
throw BadArgument ( tag ( ) + " requires <selector> -> <value>, "
" instead of: \" " + args + " \" " ) ;
cmd - > _selector = allargs . takeFirst ( ) . trimmed ( ) ;
cmd - > _value = allargs . join ( " -> " ) . trimmed ( ) ;
return cmd ;
}
bool execute ( Script * script , QWebFrame * frame ) {
Logger log ( this , script ) ;
QWebElement element ( find ( frame , script - > replacevars ( _selector ) ) ) ;
if ( element . isNull ( ) )
throw ElementNotFound ( script - > replacevars ( _selector ) ) ;
QString value ( script - > replacevars ( _value ) ) ;
if ( element . tagName ( ) = = " SELECT " ) {
// value is a comma seperated list of option values
QStringList values ;
switch ( value . size ( ) > 1 & & value . at ( 0 ) = = value . at ( value . size ( ) - 1 )
? value . at ( 0 ) . toLatin1 ( ) : ' \0 ' ) {
case ' " ' : case ' \' ' : {
values = value . mid ( 1 , value . size ( ) - 2 )
. split ( QRegularExpression ( QString ( value [ 0 ] ) + " , * "
+ QString ( value [ 0 ] ) ) ) ;
} break ;
default : {
values = value . split ( QRegularExpression ( " , * " ) ) ;
}
}
Q_FOREACH ( QWebElement option , element . findAll ( " option " ) ) {
QString name ( option . evaluateJavaScript ( " this.value " ) . toString ( ) ) ;
option . evaluateJavaScript
( " this.selected= " + QString ( values . contains ( name ) ? " true; " : " false; " ) ) ;
}
} else {
if ( value . size ( ) > 1 & & value [ 0 ] = = value [ value . size ( ) - 1 ] & &
( value [ 0 ] = = ' " ' | | value [ 0 ] = = ' \' ' ) )
element . evaluateJavaScript ( " this.value= " + value + " ; " ) ;
else
element . evaluateJavaScript ( " this.value=' "
+ value . replace ( " ' " , " \\ ' " )
+ " '; " ) ;
}
return true ;
}
private :
QString _selector ;
QString _value ;
} ;
/* Template:
class : public Command {
public :
@ -1530,12 +1738,12 @@ class : public Command {
}
QString description ( ) const {
return
" "
tag ( ) +
" \n \n "
" " ;
}
QString command ( ) const {
return " " ;
return tag ( ) ;
}
std : : shared_ptr < Command > parse ( Script * , QString args ,
QStringList & in , int ) {
@ -1555,7 +1763,7 @@ inline bool Screenshot::execute(Script* script, QWebFrame* frame) {
QString filename ( screenshot ( line ( ) , targetdir ( ) ,
QFileInfo ( testsuite ( ) ) . baseName ( ) ,
_filename , frame ) ) ;
log . plainlog ( " [[ATTACHMENT| " + filename + " ]] " ) ;
log [ " [[ATTACHMENT| " + filename + " ]] " ] ;
return true ;
}
@ -1566,11 +1774,11 @@ inline Logger::Logger(Command* command, Script* script):
_script - > log ( _command - > command ( ) ) ;
}
}
inline void Logger : : log ( QString txt ) {
inline void Logger : : operator ( ) ( QString txt ) {
if ( _command - > log ( ) )
_script - > log ( txt ) ;
}
inline void Logger : : plainlog ( QString txt ) {
inline void Logger : : operator [ ] ( QString txt ) {
_script - > plainlog ( txt ) ;
}
inline Logger : : ~ Logger ( ) {
@ -1599,6 +1807,8 @@ inline void Script::initPrototypes() {
add ( new Timeout ) ;
add ( new CaCertificate ) ;
add ( new ClientCertificate ) ;
add ( new : : ClickType ) ;
add ( new SetValue ) ;
}
# endif