Test your websites with this simple GUI based scripted webtester. Generate simple testscripts directly from surfng on the webpage, enhance them with your commands, with variables, loops, checks, … and finally run automated web tests.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

248 lines
9.1 KiB

#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&);
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<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& 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