/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef TESTGUI_HXX
# define TESTGUI_HXX
# include <webpage.hxx>
# include <commands.hxx>
# include <QMainWindow>
# include <QSettings>
# include <QWebFrame>
# include <QWebElement>
# include <QFileDialog>
# include <QScrollBar>
# include <QFile>
# include <QMessageBox>
# include <ui_testgui.h>
# include <stdexcept>
# include <QNetworkReply>
class TestGUI : public QMainWindow , protected Ui : : TestGUI {
Q_OBJECT ;
public :
explicit TestGUI ( QWidget * parent = 0 , QString url = QString ( ) ) :
QMainWindow ( parent ) ,
_typing ( false ) ,
_inEventFilter ( false ) {
setupUi ( this ) ;
QSettings settings ( " mrw " , " webtester " ) ;
restoreGeometry ( settings . value ( " geometry " ) . toByteArray ( ) ) ;
restoreState ( settings . value ( " windowstate " ) . toByteArray ( ) ) ;
if ( ! url . isEmpty ( ) ) {
_url - > setText ( url ) ;
}
TestWebPage * page ( new TestWebPage ( _web ) ) ;
_web - > setPage ( page ) ;
_web - > installEventFilter ( this ) ; // track mouse and keyboard
page - > setForwardUnsupportedContent ( true ) ;
connect ( page , SIGNAL ( uploadFile ( QString ) ) , SLOT ( uploadFile ( QString ) ) ) ;
connect ( page , SIGNAL ( unsupportedContent ( QNetworkReply * ) ) ,
SLOT ( unsupportedContent ( QNetworkReply * ) ) ) ;
connect ( page , SIGNAL ( downloadRequested ( const QNetworkRequest & ) ) ,
SLOT ( downloadRequested ( const QNetworkRequest & ) ) ) ;
}
virtual ~ TestGUI ( ) { }
public Q_SLOTS :
void on__load_clicked ( ) {
enterText ( true ) ;
if ( _record - > isChecked ( ) )
appendCommand ( " load " + _url - > text ( ) ) ;
_web - > load ( _url - > text ( ) ) ;
}
void on__abort_clicked ( ) {
enterText ( true ) ;
_web - > stop ( ) ;
}
void on__actionOpen_triggered ( ) {
QString name ( QFileDialog : : getOpenFileName ( this , tr ( " Open Test Script " ) ) ) ;
if ( name . isEmpty ( ) ) return ;
on__actionRevertToSaved_triggered ( name ) ;
}
void on__actionRevertToSaved_triggered ( ) {
on__actionRevertToSaved_triggered ( _filename ) ;
}
void on__actionRevertToSaved_triggered ( QString name ) {
QFile file ( name ) ;
try {
if ( ! file . open ( QIODevice : : ReadOnly | QIODevice : : Text ) )
throw std : : runtime_error ( " file open failed " ) ;
_testscript - > setPlainText ( QString : : fromUtf8 ( file . readAll ( ) ) ) ;
if ( file . error ( ) ! = QFileDevice : : NoError )
throw std : : runtime_error ( " file read failed " ) ;
_filename = name ;
_actionSave - > setEnabled ( true ) ;
_actionRevertToSaved - > setEnabled ( true ) ;
} catch ( const std : : exception & x ) {
QMessageBox : : critical ( this , tr ( " Open Failed " ) ,
tr ( " Reading test script failed, %2. "
" Cannot read test script from file %1. " )
. arg ( name ) . arg ( x . what ( ) ) ) ;
}
}
void on__actionSaveAs_triggered ( ) {
QString name ( QFileDialog : : getSaveFileName ( this , tr ( " Save Test Script " ) ) ) ;
if ( name . isEmpty ( ) ) return ;
_filename = name ;
on__actionSave_triggered ( ) ;
}
void on__actionSave_triggered ( ) {
QFile file ( _filename ) ;
try {
if ( ! file . open ( QIODevice : : WriteOnly | QIODevice : : Text ) )
throw std : : runtime_error ( " file open failed " ) ;
QTextStream out ( & file ) ;
out < < _testscript - > toPlainText ( ) ;
if ( out . status ( ) ! = QTextStream : : Ok )
throw std : : runtime_error ( std : : string ( " file write failed ( " )
+ char ( out . status ( ) + 48 ) + " ) " ) ;
_actionSave - > setEnabled ( true ) ;
_actionRevertToSaved - > setEnabled ( true ) ;
} catch ( const std : : exception & x ) {
QMessageBox : : critical ( this , tr ( " Save Failed " ) ,
tr ( " Saving test script failed, %2. "
" Cannot write test script to file %1. " )
. arg ( _filename ) . arg ( x . what ( ) ) ) ;
}
}
void on__actionClear_triggered ( ) {
_testscript - > clear ( ) ;
_log - > clear ( ) ;
_filename . clear ( ) ;
_actionSave - > setEnabled ( false ) ;
_actionRevertToSaved - > setEnabled ( false ) ;
}
void on__run_clicked ( ) {
bool oldRecordState ( _record - > isChecked ( ) ) ;
_run - > setEnabled ( false ) ;
try {
xml : : Node testsuites ( " testsuites " ) ;
xml : : Node testsuite ( " testsuite " ) ;
testsuite . attr ( " name " ) = " on-the-fly " ;
testsuite . attr ( " timestamp " ) =
QDateTime : : currentDateTime ( ) . toString ( Qt : : ISODate ) . toStdString ( ) ;
xml : : Node testcase ( " testcase " ) ;
testcase . attr ( " classname " ) = " testsuite-preparation " ;
QString text ( _testscript - > textCursor ( ) . selectedText ( ) ) ;
if ( text . isEmpty ( ) ) text = _testscript - > toPlainText ( ) ;
Script script ;
connect ( & script , SIGNAL ( logging ( QString ) ) , SLOT ( logging ( QString ) ) ) ;
script . parse ( text . split ( ' \n ' ) ) ;
script . run ( _web - > page ( ) - > mainFrame ( ) , testsuite , QString ( ) , false ) ;
} catch ( std : : exception & x ) {
QMessageBox : : critical ( this , tr ( " Script Failed " ) ,
tr ( " Script failed with message: \n %1 " )
. arg ( x . what ( ) ) ) ;
}
_run - > setEnabled ( true ) ;
_record - > setChecked ( oldRecordState ) ;
}
void on__focused_clicked ( ) {
enterText ( true ) ;
QWebElement element ( focused ( ) ) ;
if ( element . isNull ( ) ) return ;
highlight ( element ) ;
_focusedText - > setText ( selector ( element ) ) ;
}
void on__select_clicked ( ) {
enterText ( true ) ;
highlight ( _web - > page ( ) - > mainFrame ( ) - > documentElement ( )
. findFirst ( _selector - > text ( ) ) ) ;
}
void on__jsClick_clicked ( ) {
enterText ( true ) ;
execute ( selector ( ) ,
" this.click(); " ) ;
// "var evObj = document.createEvent('MouseEvents');\n"
// "evObj.initEvent( 'click', true, true );\n"
// "this.dispatchEvent(evObj);");
}
void on__jsValue_clicked ( ) {
enterText ( true ) ;
QWebElement element ( selected ( ) ) ;
execute ( selector ( element ) ,
" this.value=' " + value ( element ) . replace ( " \n " , " \\ n " ) + " '; " ) ;
}
void on__jsExecute_clicked ( ) {
enterText ( true ) ;
execute ( selector ( ) , _javascriptCode - > toPlainText ( ) ) ;
}
void on__web_linkClicked ( const QUrl & url ) {
enterText ( true ) ;
if ( _record - > isChecked ( ) )
appendCommand ( " load " + url . url ( ) ) ;
}
void on__web_loadProgress ( int progress ) {
enterText ( true ) ;
_progress - > setValue ( progress ) ;
}
void on__web_loadStarted ( ) {
enterText ( true ) ;
if ( _record - > isChecked ( ) )
appendCommand ( " expect loadStarted " ) ;
_progress - > setValue ( 0 ) ;
_urlStack - > setCurrentIndex ( PROGRESS_VIEW ) ;
}
void on__web_statusBarMessage ( const QString & ) {
//std::cout<<"statusBarMessage: "<<text.toStdString()<<std::endl;
}
void on__web_titleChanged ( const QString & ) {
//std::cout<<"titleChanged: "<<title.toStdString()<<std::endl;
}
void on__web_urlChanged ( const QUrl & url ) {
enterText ( true ) ;
if ( _record - > isChecked ( ) )
appendCommand ( " expect urlChanged " + url . url ( ) ) ;
}
void on__web_selectionChanged ( ) {
_source - > setPlainText ( _web - > hasSelection ( )
? _web - > selectedHtml ( )
: _web - > page ( ) - > mainFrame ( ) - > toHtml ( ) ) ;
}
void on__web_loadFinished ( bool ok ) {
enterText ( true ) ;
if ( _record - > isChecked ( ) )
appendCommand ( " expect loadFinished "
+ QString ( ok ? " true " : " false " ) ) ;
_urlStack - > setCurrentIndex ( URL_VIEW ) ;
on__web_selectionChanged ( ) ;
setLinks ( ) ;
setForms ( ) ;
setDom ( ) ;
}
void on__forms_currentItemChanged ( QTreeWidgetItem * item , QTreeWidgetItem * ) {
if ( ! item ) return ;
_source - > setPlainText ( item - > data ( 0 , Qt : : UserRole ) . toString ( ) ) ;
}
void on__dom_currentItemChanged ( QTreeWidgetItem * item , QTreeWidgetItem * ) {
if ( ! item ) return ;
_source - > setPlainText ( item - > data ( 0 , Qt : : UserRole ) . toString ( ) ) ;
}
void uploadFile ( QString filename ) {
enterText ( true ) ;
if ( _record - > isChecked ( ) )
appendCommand ( " upload " + filename ) ;
}
void unsupportedContent ( QNetworkReply * reply ) {
if ( ! _record - > isChecked ( ) ) return ;
QString filename ( reply - > url ( ) . toString ( ) . split ( ' / ' ) . last ( ) ) ;
if ( reply - > header ( QNetworkRequest : : ContentDispositionHeader ) . isValid ( ) ) {
QString part ( reply - > header ( QNetworkRequest : : ContentDispositionHeader )
. toString ( ) ) ;
if ( part . contains ( QRegularExpression ( " attachment; *filename= " ) ) ) {
part . replace ( QRegularExpression ( " .*attachment; *filename= " ) , " " ) ;
if ( part . size ( ) ) filename = part ;
}
}
QString text ( _testscript - > toPlainText ( ) ) ;
int pos1 ( text . lastIndexOf ( QRegularExpression ( " ^do " ) ) ) ;
int pos2 ( text . lastIndexOf ( QRegularExpression ( " ^load " ) ) ) ;
text . insert ( pos1 > pos2 ? pos1 : pos2 , " download " + filename ) ;
_testscript - > setPlainText ( text ) ;
_testscript - > moveCursor ( QTextCursor : : End ) ;
_testscript - > ensureCursorVisible ( ) ;
}
void downloadRequested ( const QNetworkRequest & ) {
if ( _record - > isChecked ( ) )
appendCommand ( " download2 " ) ;
}
void logging ( const QString & txt ) {
_log - > appendPlainText ( txt ) ;
QScrollBar * vb ( _log - > verticalScrollBar ( ) ) ;
if ( ! vb ) return ;
vb - > setValue ( vb - > maximum ( ) ) ;
}
void appendCommand ( const QString & txt ) {
_testscript - > appendPlainText ( txt ) ;
QScrollBar * vb ( _testscript - > verticalScrollBar ( ) ) ;
if ( ! vb ) return ;
vb - > setValue ( vb - > maximum ( ) ) ;
}
protected :
void closeEvent ( QCloseEvent * event ) {
QSettings settings ( " mrw " , " webtester " ) ;
settings . setValue ( " geometry " , saveGeometry ( ) ) ;
settings . setValue ( " windowstate " , saveState ( ) ) ;
QMainWindow : : closeEvent ( event ) ;
}
bool eventFilter ( QObject * , QEvent * event ) {
if ( _inEventFilter ) return false ;
_inEventFilter = true ;
enterText ( ) ;
QWebElement element ( focused ( dynamic_cast < QMouseEvent * > ( event ) ) ) ;
switch ( event - > type ( ) ) {
case QEvent : : KeyPress : {
QKeyEvent * k ( dynamic_cast < QKeyEvent * > ( event ) ) ;
switch ( k - > key ( ) ) {
case Qt : : Key_Tab :
case Qt : : Key_Backtab : {
enterText ( true ) ;
} break ;
case Qt : : Key_Backspace : {
_keyStrokes . chop ( 1 ) ;
} break ;
case Qt : : Key_Shift : break ;
case Qt : : Key_Enter :
case Qt : : Key_Return : {
_keyStrokes + = " \\ n " ;
_lastFocused = element ;
_typing = true ;
} break ;
default : {
_keyStrokes + = k - > text ( ) ;
_lastFocused = element ;
_typing = true ;
}
}
} break ;
case QEvent : : MouseButtonRelease : {
enterText ( true ) ;
_lastFocused = element ;
if ( _record - > isChecked ( ) & & ! element . isNull ( ) ) {
QString selected ( selector ( _lastFocused ) ) ;
QRegularExpressionMatch mooCombo
( QRegularExpression ( " ^(#jform_[_A-Za-z0-9]+)_chzn>.*$ " )
. match ( selected ) ) ;
QRegularExpressionMatch mooComboItem
( QRegularExpression
( " ^li \\ .highlighted( \\ .result-selected)? \\ .active-result$ " )
. match ( selected ) ) ;
if ( mooCombo . hasMatch ( ) ) {
// special treatment for moo tools combobox (e.g. used in joomla)
appendCommand ( " click " + mooCombo . captured ( 1 ) + " >a " ) ;
appendCommand ( " sleep 1 " ) ;
} else if ( mooComboItem . hasMatch ( ) ) {
// special treatment for item in moo tools combobox
appendCommand
( " click li.active-result[data-option-array-index= \" "
+ element . attribute ( " data-option-array-index " ) + " \" ] " ) ;
appendCommand ( " sleep 1 " ) ;
} else {
appendCommand ( " click " + selected ) ;
}
}
} break ;
case QEvent : : InputMethodQuery :
case QEvent : : ToolTipChange :
case QEvent : : MouseMove :
case QEvent : : UpdateLater :
case QEvent : : Paint : break ;
default : ; //LOG("Event: "<<event->type());
}
_inEventFilter = false ;
return false ;
}
private :
void enterText ( bool force = false ) {
if ( ! force & & ( ! _typing | | _lastFocused = = focused ( ) ) ) return ;
if ( _keyStrokes . size ( ) & & ! _lastFocused . isNull ( ) ) {
store ( selector ( _lastFocused ) , " this.value=' "
+ value ( _lastFocused ) . replace ( " \n " , " \\ n " ) + " '; " ) ;
}
_lastFocused = QWebElement ( ) ;
_keyStrokes . clear ( ) ;
_typing = false ;
}
QWebElement selected ( ) {
return _web - > page ( ) - > mainFrame ( ) - > documentElement ( ) . findFirst ( selector ( ) ) ;
}
QString selector ( ) {
if ( _takeFocused - > isChecked ( ) )
return selector ( focused ( ) ) ;
else if ( _takeSelect - > isChecked ( ) )
return _selector - > text ( ) ;
else
return QString ( ) ; // error
}
void highlight ( QWebElement element ) {
element
. evaluateJavaScript ( " var selection = window.getSelection(); "
" selection.setBaseAndExtent(this, 0, this, 1); " ) ;
}
QWebElement focused ( QMouseEvent * event = 0 ) {
Q_FOREACH ( QWebElement element ,
_web - > page ( ) - > currentFrame ( ) - > findAllElements ( " * " ) ) {
if ( element . hasFocus ( ) ) {
return element ;
}
}
if ( event ) { // try to find element using mouse position
QWebFrame * frame ( _web - > page ( ) - > frameAt ( event - > pos ( ) ) ) ;
if ( frame ) {
QWebHitTestResult hit ( frame - > hitTestContent ( event - > pos ( ) ) ) ;
if ( ! hit . element ( ) . isNull ( ) )
return hit . element ( ) ;
if ( ! hit . enclosingBlockElement ( ) . isNull ( ) )
return hit . enclosingBlockElement ( ) ;
}
}
return QWebElement ( ) ;
}
bool unique ( QString selector ) {
return _web - > page ( ) - > mainFrame ( ) - > findAllElements ( selector ) . count ( ) = = 1 ;
}
QString quote ( QString txt ) {
if ( txt . contains ( ' " ' ) ) return " ' " + txt + " ' " ;
return ' " ' + txt + ' " ' ;
}
QString selector ( const QWebElement & element ) {
if ( element . isNull ( ) ) return QString ( ) ;
if ( element . hasAttribute ( " id " ) & & unique ( " # " + element . attribute ( " id " ) ) ) {
return " # " + element . attribute ( " id " ) ;
} else if ( element . hasAttribute ( " name " ) & &
unique ( element . tagName ( ) . toLower ( )
+ " [name= " + quote ( element . attribute ( " name " ) ) + " ] " ) ) {
return element . tagName ( ) . toLower ( )
+ " [name= " + quote ( element . attribute ( " name " ) ) + " ] " ;
} else {
QString res ;
Q_FOREACH ( QString attr , element . attributeNames ( ) ) {
if ( attr = = " id " )
res = " # " + element . attribute ( " id " ) + res ;
else if ( attr = = " class " )
Q_FOREACH ( QString c , element . attribute ( attr ) . split ( ' ' ) ) {
if ( ! c . isEmpty ( ) ) res = ' . ' + c + res ;
}
else if ( element . attribute ( attr ) . isEmpty ( ) )
res + = " [ " + attr + " ] " ;
else
res + = " [ " + attr + " = " + quote ( element . attribute ( attr ) ) + " ] " ;
if ( unique ( element . tagName ( ) . toLower ( ) + res ) )
return element . tagName ( ) . toLower ( ) + res ;
}
QString p ( selector ( element . parent ( ) ) ) ;
if ( unique ( p + " > " + element . tagName ( ) . toLower ( ) + res ) )
return p + " > " + element . tagName ( ) . toLower ( ) + res ;
QString s ( selector ( element . previousSibling ( ) ) ) ;
if ( unique ( s + " + " + element . tagName ( ) . toLower ( ) + res ) )
return s + " + " + element . tagName ( ) . toLower ( ) + res ;
if ( ! p . isEmpty ( ) )
return p + " > " + element . tagName ( ) . toLower ( ) + res ;
if ( ! s . isEmpty ( ) )
return s + " + " + element . tagName ( ) . toLower ( ) + res ;
return element . tagName ( ) . toLower ( ) + res ;
}
}
QString value ( QWebElement element ) {
return element . evaluateJavaScript ( " this.value " ) . toString ( ) ;
//! @bug Bug in Qt, attribute("value") is always empty
// if (element.hasAttribute("value"))
// return element.attribute("value");
// else
// return element.toPlainText();
}
void store ( const QString & selector , QString code ) {
if ( _record - > isChecked ( ) )
appendCommand ( " do " + selector + " \n "
+ code . replace ( " \n " , " \\ n " ) ) ;
}
void execute ( const QString & selector , const QString & code ) {
store ( selector , code ) ;
_web - > page ( ) - > mainFrame ( ) - > documentElement ( ) . findFirst ( selector )
. evaluateJavaScript ( code ) ;
}
void setLinks ( ) {
QWebElementCollection links ( _web - > page ( ) - > mainFrame ( ) - > documentElement ( )
. findAll ( " a " ) ) ;
_links - > setRowCount ( links . count ( ) ) ;
for ( int row ( 0 ) ; row < _links - > rowCount ( ) ; + + row ) {
{
QTableWidgetItem * item ( new QTableWidgetItem ( ) ) ;
item - > setText ( links [ row ] . attribute ( " href " ) ) ;
_links - > setItem ( row , 0 , item ) ;
} {
QTableWidgetItem * item ( new QTableWidgetItem ( ) ) ;
item - > setText ( links [ row ] . hasAttribute ( " title " )
? links [ row ] . attribute ( " title " )
: links [ row ] . toInnerXml ( ) ) ;
_links - > setItem ( row , 1 , item ) ;
}
_links - > horizontalHeader ( ) - > resizeSections ( QHeaderView : : Stretch ) ;
}
}
void setForms ( ) {
QWebElementCollection forms ( _web - > page ( ) - > mainFrame ( ) - > documentElement ( )
. findAll ( " form " ) ) ;
_forms - > clear ( ) ;
Q_FOREACH ( const QWebElement & form , forms ) {
addDomElement ( form , _forms - > invisibleRootItem ( ) ) ;
}
}
void setDom ( ) {
_dom - > clear ( ) ;
addDomElement ( _web - > page ( ) - > mainFrame ( ) - > documentElement ( ) ,
_dom - > invisibleRootItem ( ) ) ;
}
//void addDomChildren(const QWebElement&, QTreeWidgetItem*);
void addDomElement ( const QWebElement & element ,
QTreeWidgetItem * parent ) {
QTreeWidgetItem * item ( new QTreeWidgetItem ( ) ) ;
item - > setText ( 0 , element . tagName ( ) ) ;
item - > setData ( 0 , Qt : : UserRole , element . toOuterXml ( ) ) ;
parent - > addChild ( item ) ;
addDomChildren ( element , item ) ;
}
void addDomChildren ( const QWebElement & parentElement ,
QTreeWidgetItem * parentItem ) {
for ( QWebElement element = parentElement . firstChild ( ) ;
! element . isNull ( ) ;
element = element . nextSibling ( ) ) {
addDomElement ( element , parentItem ) ;
}
}
private :
enum UrlStack {
URL_VIEW = 0 ,
PROGRESS_VIEW
} ;
private :
QString _filename ;
QWebElement _lastFocused ; // cache for last focussed element
QString _keyStrokes ; // collect key strokes
bool _typing ; // user is typing
bool _inEventFilter ; // actually handling event filter
} ;
# endif // TESTGUI_HXX