# ifndef __SCRIPTFILE__HXX
# define __SCRIPTFILE__HXX
# include <commands.hxx>
# include <ui_scriptfile.hxx>
# include <QMessageBox>
# include <QScrollBar>
# include <QTextDocumentFragment>
# include <cassert>
class ScriptFile : public QDockWidget , protected Ui : : ScriptFile {
Q_OBJECT
Q_SIGNALS :
void modified ( ScriptFile * ) ;
void link ( QString ) ;
void include ( QString ) ;
void close ( ScriptFile * ) ;
void run ( const QString & , const QString & , bool , Script & ) ;
void running ( QString file , int line ) ;
public :
ScriptFile ( QWidget * p = nullptr ) : QDockWidget ( p ) {
setupUi ( this ) ;
assert ( connect ( _editor , SIGNAL ( textChanged ( ) ) , SLOT ( modified ( ) ) ) ) ;
assert ( connect ( _editor , SIGNAL ( include ( QString ) ) , SIGNAL ( include ( QString ) ) ) ) ;
assert ( connect ( _editor , SIGNAL ( link ( QString ) ) , SIGNAL ( link ( QString ) ) ) ) ;
_searchBar - > hide ( ) ;
_replaceBar - > hide ( ) ;
_lineBar - > hide ( ) ;
_progress - > hide ( ) ;
_status - > setCurrentIndex ( STATUS_NONE ) ;
}
CodeEditor * editor ( ) {
return _editor ;
}
QString name ( ) {
return _name ;
}
void name ( QString name ) {
_name = name ;
setWindowTitle ( name + " [*] " ) ;
setWindowModified ( false ) ;
}
void gotoLine ( int line ) {
_editor - > gotoLine ( line ) ;
}
public Q_SLOTS :
void load ( QString name = QString ( ) ) {
if ( isWindowModified ( ) & &
QMessageBox : : question ( this , tr ( " Changes Not Saved " ) ,
tr ( " Load script without saving changes? " ) )
! = QMessageBox : : Yes )
return ;
QString oldname ( _name ) ;
if ( ! name . isEmpty ( ) ) _name = name ;
QFileInfo info ( name ) ;
if ( info . absoluteDir ( ) = = QDir : : current ( ) ) _name = info . fileName ( ) ;
try {
QFile file ( _name ) ;
if ( ! file . open ( QIODevice : : ReadOnly | QIODevice : : Text ) )
throw std : : runtime_error ( " file open failed " ) ;
_editor - > setPlainText ( QString : : fromUtf8 ( file . readAll ( ) ) ) ;
if ( file . error ( ) ! = QFileDevice : : NoError )
throw std : : runtime_error ( " file read failed " ) ;
setWindowTitle ( _name + " [*] " ) ;
setWindowModified ( false ) ;
_status - > setCurrentIndex ( STATUS_NONE ) ;
} 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 ( ) ) ) ;
_name = oldname ;
}
}
void save ( QString name = QString ( ) ) {
QString oldname ( _name ) ;
if ( ! name . isEmpty ( ) ) _name = name ;
QFile file ( _name ) ;
try {
if ( ! file . open ( QIODevice : : WriteOnly | QIODevice : : Text ) )
throw std : : runtime_error ( " file open failed " ) ;
QTextStream out ( & file ) ;
out < < _editor - > toPlainText ( ) ;
if ( out . status ( ) ! = QTextStream : : Ok )
throw std : : runtime_error ( std : : string ( " file write failed ( " )
+ char ( out . status ( ) + 48 ) + " ) " ) ;
setWindowModified ( false ) ;
setWindowTitle ( _name + " [*] " ) ;
} 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 ( _name ) . arg ( x . what ( ) ) ) ;
_name = oldname ;
}
}
void clear ( ) {
if ( isWindowModified ( ) & &
QMessageBox : : question ( this , tr ( " Changes Not Saved " ) ,
tr ( " Clear script without saving changes? " ) )
! = QMessageBox : : Yes )
return ;
_editor - > clear ( ) ;
setWindowModified ( false ) ;
}
void modified ( ) {
setWindowModified ( true ) ;
modified ( this ) ;
}
void run ( ) {
_progress - > reset ( ) ;
_progress - > show ( ) ;
_status - > setCurrentIndex ( STATUS_RUNNING ) ;
bool oldRecordState ( _record - > isChecked ( ) ) ;
_record - > setChecked ( false ) ;
_record - > setEnabled ( false ) ;
_run - > setEnabled ( false ) ;
Script script ;
try {
assert ( connect ( & script , SIGNAL ( progress ( QString , int , int , int ) ) ,
SLOT ( progress ( QString , int , int , int ) ) ) ) ;
assert ( connect ( & script , SIGNAL ( progress ( QString , int , int , int ) ) ,
SIGNAL ( running ( QString , int ) ) ) ) ;
QString text ( _editor - > textCursor ( ) . selection ( ) . toPlainText ( ) ) ;
if ( text . isEmpty ( ) ) text = _editor - > toPlainText ( ) ;
run ( _name , text , _screenshots - > isChecked ( ) , script ) ;
_status - > setCurrentIndex ( STATUS_SUCCESS ) ;
} catch ( std : : exception & x ) {
_status - > setCurrentIndex ( STATUS_ERROR ) ;
std : : shared_ptr < Command > cmd ( script . command ( ) ) ;
if ( cmd )
QMessageBox : : critical ( this ,
tr ( " Test Failed " ) ,
tr ( " <html> "
" <h1>Error [%1]</h1> "
" <dl> "
" <dt>Command:</dt><dd><code>%3</code></dd> "
" <dt>File:</dt><dd>%4</dd> "
" <dt>Line:</dt><dd>%5</dd> "
" <dt>Error Message:</dt><dd><pre>%2</pre></dd> "
" </dl> "
" </html> " )
. arg ( demangle ( typeid ( x ) . name ( ) ) )
. arg ( x . what ( ) )
. arg ( cmd - > command ( ) )
. arg ( cmd - > file ( ) )
. arg ( cmd - > line ( ) ) ) ;
else
QMessageBox : : critical ( this ,
tr ( " Test Failed " ) ,
tr ( " <html> "
" <h1>Error [%1]</h1> "
" <p><code>%2</code></p> "
" </html> " )
. arg ( demangle ( typeid ( x ) . name ( ) ) )
. arg ( QString ( x . what ( ) ) . replace ( " \n " , " <br/> " ) ) ) ;
}
_run - > setEnabled ( true ) ;
_record - > setEnabled ( true ) ;
_record - > setChecked ( oldRecordState ) ;
_progress - > hide ( ) ;
}
void appendCommand ( const QString & txt ) {
if ( ! _record - > isChecked ( ) ) return ;
_editor - > appendPlainText ( txt ) ;
QScrollBar * vb ( _editor - > verticalScrollBar ( ) ) ;
_editor - > moveCursor ( QTextCursor : : End ) ;
_editor - > ensureCursorVisible ( ) ;
if ( ! vb ) return ;
vb - > setValue ( vb - > maximum ( ) ) ;
}
void appendCommand ( const QString & selector , const QString & txt ) {
if ( ! _record - > isChecked ( ) ) return ;
QString text ( _editor - > toPlainText ( ) ) ;
QStringList lines ( text . split ( " \n " ) ) ;
bool changed ( false ) ;
while ( lines . size ( ) & &
( lines . last ( ) = = " click " + selector | |
lines . last ( ) . startsWith ( " setvalue " + selector + " -> " ) ) ) {
lines . removeLast ( ) ;
changed = true ;
}
if ( changed ) {
_editor - > setPlainText ( lines . join ( " \n " ) ) ;
_editor - > moveCursor ( QTextCursor : : End ) ;
_editor - > ensureCursorVisible ( ) ;
}
appendCommand ( txt ) ;
}
void appendWebLoadFinished ( bool ok ) {
if ( ! _record - > isChecked ( ) ) return ;
QString text ( _editor - > toPlainText ( ) ) ;
QStringList lines ( text . split ( " \n " ) ) ;
if ( ok & & lines . size ( ) > 1 & &
lines . last ( ) . startsWith ( " expect urlChanged " ) & &
lines . at ( lines . size ( ) - 2 ) = = " expect loadStarted " ) {
// replace three expect lines by one single line
QString url ( lines . last ( ) . replace ( " expect urlChanged " , " " ) . trimmed ( ) ) ;
lines . removeLast ( ) ; lines . removeLast ( ) ;
_editor - > setPlainText ( lines . join ( " \n " ) ) ;
_editor - > moveCursor ( QTextCursor : : End ) ;
_editor - > ensureCursorVisible ( ) ;
appendCommand ( " expect load " + url ) ;
} else {
appendCommand ( " expect loadFinished " + QString ( ok ? " true " : " false " ) ) ;
}
}
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 ( _editor - > toPlainText ( ) ) ;
int pos1 ( text . lastIndexOf ( QRegularExpression ( " ^do " ) ) ) ;
int pos2 ( text . lastIndexOf ( QRegularExpression ( " ^load " ) ) ) ;
int pos3 ( text . lastIndexOf ( QRegularExpression ( " ^click " ) ) ) ;
text . insert ( std : : max ( { pos1 , pos2 , pos3 } ) , " download " + filename ) ;
_editor - > setPlainText ( text ) ;
_editor - > moveCursor ( QTextCursor : : End ) ;
_editor - > ensureCursorVisible ( ) ;
}
void progress ( const QString & file , int line , int pos , int max ) {
_progress - > setFormat ( QString ( " %1:%2 — %p% " ) . arg ( file ) . arg ( line ) ) ;
_progress - > setMinimum ( 0 ) ;
_progress - > setMaximum ( max ) ;
_progress - > setValue ( pos ) ;
}
void runEnabled ( bool f = true ) {
_run - > setEnabled ( false ) ;
}
void on__run_clicked ( ) {
run ( ) ;
}
protected :
void closeEvent ( QCloseEvent * ) {
close ( this ) ;
}
private :
enum RunStatus {
STATUS_NONE = 0 ,
STATUS_RUNNING ,
STATUS_SUCCESS ,
STATUS_ERROR
} ;
private :
QString _name ;
} ;
# endif