# ifndef __SCRIPTFILE__HXX
# define __SCRIPTFILE__HXX
# include <commands.hxx>
# include <ui_scriptfile.hxx>
# include <QMessageBox>
# include <QScrollBar>
# include <QShortcut>
# 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 progress ( QString , int , int , int ) ;
public :
ScriptFile ( QWidget * p = nullptr , QString name = QString ( ) ) : QDockWidget ( p ) , _name ( name ) {
setupUi ( this ) ;
setWindowTitle ( _name + " [*] " ) ;
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 ) ) ) ) ;
assert ( connect ( _editor , SIGNAL ( cursorPositionChanged ( ) ) , SLOT ( setLine ( ) ) ) ) ;
assert ( connect ( _line , SIGNAL ( valueChanged ( int ) ) , SLOT ( gotoLine ( int ) ) ) ) ;
_searchBar - > hide ( ) ;
_replaceBar - > hide ( ) ;
_status - > setCurrentIndex ( STATUS_NONE ) ;
}
CodeEditor * editor ( ) {
return _editor ;
}
QString name ( ) {
return _name ;
}
void name ( QString name ) {
_name = name ;
setWindowTitle ( name + " [*] " ) ;
setWindowModified ( false ) ;
}
public Q_SLOTS :
void setLine ( ) {
int pos ( _editor - > textCursor ( ) . blockNumber ( ) + 1 ) ;
if ( pos ) _line - > setValue ( pos ) ;
}
void gotoLine ( int line ) {
_editor - > gotoLine ( line ) ;
}
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 ( ) {
_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 ) ) ,
SIGNAL ( progress ( QString , int , int , 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 ) ;
}
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 runEnabled ( bool f = true ) {
_run - > setEnabled ( f ) ;
}
void on__run_clicked ( ) {
run ( ) ;
}
void on__from_textEdited ( const QString & txt ) {
on__next_clicked ( ) ;
}
void on__next_clicked ( ) {
if ( _regex - > isChecked ( ) )
_editor - > find ( QRegExp ( _from - > text ( ) ) ) ;
else
_editor - > find ( _from - > text ( ) ) ;
}
void on__previous_clicked ( ) {
if ( _regex - > isChecked ( ) )
_editor - > find ( QRegExp ( _from - > text ( ) ) , QTextDocument : : FindBackward ) ;
else
_editor - > find ( _from - > text ( ) , QTextDocument : : FindBackward ) ;
}
void on__replace_clicked ( ) {
QTextCursor tc ( _editor - > textCursor ( ) ) ;
if ( tc . hasSelection ( ) ) tc . insertText ( _to - > text ( ) ) ;
}
void on__replaceAll_clicked ( ) {
QTextCursor cursor ( _editor - > textCursor ( ) ) ;
if ( _regex - > isChecked ( ) )
_editor - > setPlainText ( _editor - > toPlainText ( ) . replace ( QRegExp ( _from - > text ( ) ) , _to - > text ( ) ) ) ;
else
_editor - > setPlainText ( _editor - > toPlainText ( ) . replace ( _from - > text ( ) , _to - > text ( ) ) ) ;
_editor - > setTextCursor ( cursor ) ;
}
void startSearch ( ) {
_searchBar - > show ( ) ;
_from - > setFocus ( ) ;
_from - > selectAll ( ) ;
}
void startReplace ( ) {
_searchBar - > show ( ) ;
_replaceBar - > show ( ) ;
_to - > setFocus ( ) ;
_to - > selectAll ( ) ;
}
protected :
void closeEvent ( QCloseEvent * ) {
close ( this ) ;
}
void keyPressEvent ( QKeyEvent * e ) {
if ( e - > matches ( QKeySequence : : Find ) )
startSearch ( ) ;
if ( e - > matches ( QKeySequence : : Replace ) )
startReplace ( ) ;
if ( e - > matches ( QKeySequence : : FindNext ) )
on__next_clicked ( ) ;
if ( e - > matches ( QKeySequence : : FindPrevious ) )
on__previous_clicked ( ) ;
if ( e - > matches ( QKeySequence : : Cancel ) ) {
_searchBar - > hide ( ) ;
_replaceBar - > hide ( ) ;
}
return QDockWidget : : keyPressEvent ( e ) ;
}
private :
enum RunStatus {
STATUS_NONE = 0 ,
STATUS_RUNNING ,
STATUS_SUCCESS ,
STATUS_ERROR
} ;
private :
QString _name ;
} ;
# endif