#ifndef __SCRIPTFILE__HXX #define __SCRIPTFILE__HXX #include #include #include #include #include #include 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&); 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); } 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)), SLOT(progress(QString, 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 cmd(script.command()); if (cmd) QMessageBox::critical(this, tr("Test Failed"), tr("" "

Error [%1]

" "
" "
Command:
%3
" "
File:
%4
" "
Line:
%5
" "
Error Message:
%2
" "
" "") .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("" "

Error [%1]

" "

%2

" "") .arg(demangle(typeid(x).name())) .arg(QString(x.what()).replace("\n", "
"))); } _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& txt, int pos, int max) { _progress->setFormat(QString("%1 — %p%").arg(txt)); _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