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.
790 lines
42 KiB
790 lines
42 KiB
/*! @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.hxx> |
|
#include <stdexcept> |
|
#include <QNetworkReply> |
|
#include <QEvent> |
|
#include <QTextDocumentFragment> |
|
#include <mrw/stdext.hxx> |
|
|
|
class TestGUI: public QMainWindow, protected Ui::TestGUI { |
|
Q_OBJECT; |
|
public: |
|
explicit TestGUI(QWidget *parent = 0, |
|
QString url = QString(), |
|
QString setupScript = QString(), |
|
QString scriptFile = 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); |
|
_commands->setText(Script().commands(Script::HTML)); |
|
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&))); |
|
if (setupScript.size()) loadSetup(setupScript); |
|
if (scriptFile.size()) loadFile(scriptFile); |
|
} |
|
virtual ~TestGUI() {} |
|
public Q_SLOTS: |
|
void on__load_clicked() { |
|
enterText(true); |
|
if (_record->isChecked()) |
|
appendCommand("load "+map(_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; |
|
loadFile(name); |
|
} |
|
void on__actionOpenSetupScript_triggered() { |
|
QString name(QFileDialog::getOpenFileName(this, tr("Open Setup Script"))); |
|
if (name.isEmpty()) return; |
|
loadSetup(name); |
|
} |
|
void on__actionRevertToSaved_triggered() { |
|
loadFile(_filename); |
|
} |
|
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 { |
|
Script script; |
|
connect(&script, SIGNAL(logging(QString)), SLOT(logging(QString))); |
|
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuite")); |
|
if (_setupscriptactive->isEnabled() |
|
&& _setupscriptactive->isChecked()) { |
|
script.parse(_setupscript->toPlainText().split('\n')); |
|
script.run(_web->page()->mainFrame(), testsuites, QString(), false); |
|
script.reset(); |
|
} |
|
QString text(_testscript->textCursor().selection().toPlainText()); |
|
if (text.isEmpty()) text = _testscript->toPlainText(); |
|
script.parse(text.split('\n')); |
|
script.run(_web->page()->mainFrame(), testsuites, 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__jsExecute_clicked() { |
|
enterText(true); |
|
_jsResult->setText(execute(selector(), _javascriptCode->toPlainText())); |
|
} |
|
void on__web_linkClicked(const QUrl& url) { |
|
enterText(true); |
|
if (_record->isChecked()) |
|
appendCommand("load "+map(url.url())); |
|
} |
|
void on__web_loadProgress(int progress) { |
|
enterText(true); |
|
_progress->setValue(progress); |
|
} |
|
void on__web_loadStarted() { |
|
enterText(true); |
|
if (_record->isChecked()) |
|
appendCommand("expect "+map("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 "+map("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()) { |
|
QString text(_testscript->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(); |
|
_testscript->setPlainText(lines.join("\n")); |
|
_testscript->moveCursor(QTextCursor::End); |
|
_testscript->ensureCursorVisible(); |
|
appendCommand("expect "+map("load "+url)); |
|
} else { |
|
appendCommand("expect "+map("loadFinished " |
|
+QString(ok?"true":"false"))); |
|
} |
|
} |
|
_urlStack->setCurrentIndex(URL_VIEW); |
|
on__web_selectionChanged(); |
|
setLinks(); |
|
setForms(); |
|
setDom(); |
|
} |
|
void on__setupscript_textChanged() { |
|
bool oldRecordState(_record->isChecked()); |
|
_run->setEnabled(false); |
|
_setupscriptactive->setEnabled(false); |
|
try { |
|
_setupscriptstatus->setText(trUtf8("?")); |
|
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuite")); |
|
Script script; |
|
TestWebPage page(0, true); |
|
script.parse(_setupscript->toPlainText().split('\n')); |
|
script.run(page.mainFrame(), testsuites, QString(), false); |
|
_setupScript.cleanup(); |
|
_setupScript.parse(_setupscript->toPlainText().split('\n')); |
|
_setupScript.run(page.mainFrame(), testsuites, QString(), false);; |
|
_setupscriptstatus->setText(trUtf8("✔")); |
|
_setupscriptactive->setEnabled(true); |
|
} catch (std::exception &x) { |
|
_setupscriptstatus->setText(trUtf8("✘")); |
|
} |
|
_run->setEnabled(true); |
|
_record->setChecked(oldRecordState); |
|
} |
|
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 "+map(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 "))); |
|
int pos3(text.lastIndexOf(QRegularExpression("^click "))); |
|
text.insert(std::max({pos1, pos2, pos3}), "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()); |
|
_testscript->moveCursor(QTextCursor::End); |
|
_testscript->ensureCursorVisible(); |
|
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()) { |
|
if (!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 "+map(mooCombo.captured(1)+">a")); |
|
appendCommand("sleep "+map("1")); |
|
} else if (mooComboItem.hasMatch()) { |
|
// special treatment for item in moo tools combobox |
|
appendCommand |
|
("click "+map("li.active-result[data-option-array-index=\"" |
|
+element.attribute("data-option-array-index") |
|
+"\"]")); |
|
appendCommand("sleep "+map("1")); |
|
} else if (_lastFocused.tagName()=="SELECT") { |
|
// click on a select results in a value change |
|
// find all selected options ... |
|
QStringList v; |
|
Q_FOREACH(QWebElement option, |
|
_lastFocused.findAll("option")) { |
|
//! @bug QT does not support selected |
|
if (option.evaluateJavaScript("this.selected").toBool()) |
|
v += value(option); |
|
} |
|
setValue(selected, v); |
|
} else { |
|
appendCommand("click "+map(selected)); |
|
} |
|
if (_lastFocused.tagName()=="TEXTAREA" || |
|
_lastFocused.tagName()=="INPUT" && |
|
_lastFocused.attribute("type")=="text") { |
|
// user clickt in a text edit field, so not the klick |
|
// is important, but the text that will be typed |
|
_typing = true; |
|
} |
|
} else { |
|
appendCommand("# click, but where?"); |
|
} |
|
} |
|
} break; |
|
case QEvent::MouseButtonPress: { |
|
} break; |
|
case QEvent::ChildRemoved: { // select option value changed |
|
enterText(true); |
|
_typing = true; |
|
_lastFocused=element; |
|
} break; |
|
case QEvent::InputMethodQuery: |
|
case QEvent::ToolTipChange: |
|
case QEvent::MouseMove: |
|
case QEvent::UpdateLater: |
|
case QEvent::Paint: break; |
|
default:; |
|
/* _log->appendPlainText(QString("Event: %1") |
|
.arg(eventName(event->type()))); */ |
|
} |
|
_inEventFilter = false; |
|
return false; |
|
} |
|
private: |
|
void loadFile(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 loadSetup(QString name) { |
|
QFile file(name); |
|
try { |
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) |
|
throw std::runtime_error("file open failed"); |
|
_setupscript->setPlainText(QString::fromUtf8(file.readAll())); |
|
if (file.error()!=QFileDevice::NoError) |
|
throw std::runtime_error("file read failed"); |
|
on__setupscript_textChanged(); |
|
} 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 enterText(bool force=false) { |
|
if (!force && (!_typing || _lastFocused==focused())) return; |
|
if (_typing && !_lastFocused.isNull()) |
|
setValue(selector(_lastFocused), value(_lastFocused)); |
|
_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(); |
|
} |
|
QString map(QString in) { |
|
if (_setupscriptactive->isEnabled() |
|
&& _setupscriptactive->isChecked()) { |
|
return _setupScript.insertvars(in); |
|
} |
|
return in; |
|
} |
|
void javascript(const QString& selector, QString code) { |
|
if (_record->isChecked()) |
|
appendCommand("do "+map(selector)+"\n " |
|
+map(code).replace("\n", "\\n")); |
|
} |
|
void cleanup(const QString& selector) { |
|
QString text(_testscript->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) { |
|
_testscript->setPlainText(lines.join("\n")); |
|
_testscript->moveCursor(QTextCursor::End); |
|
_testscript->ensureCursorVisible(); |
|
} |
|
} |
|
void setValue(const QString& selector, QString code) { |
|
if (_record->isChecked()) { |
|
cleanup(selector); |
|
appendCommand("setvalue "+map(selector)+" -> '" |
|
+map(code).replace("'", "\\'").replace("\n", "\\n")+"'"); |
|
} |
|
} |
|
void setValue(const QString& selector, QStringList code) { |
|
if (_record->isChecked()) { |
|
cleanup(selector); |
|
appendCommand("setvalue "+map(selector)+" -> '"+ |
|
map(code.replaceInStrings("'", "\\'") |
|
.replaceInStrings("\n", "\\n") |
|
.join("', '")+"'")); |
|
} |
|
} |
|
QString execute(const QString& selector, const QString& code) { |
|
javascript(selector, code); |
|
return _web->page()->mainFrame()->documentElement().findFirst(selector) |
|
.evaluateJavaScript(code).toString(); |
|
} |
|
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); |
|
} |
|
} |
|
QString eventName(QEvent::Type t) { |
|
switch (t) { |
|
case QEvent::None: return "QEvent::None - Not an event."; |
|
case QEvent::ActionAdded: return "QEvent::ActionAdded - A new action has been added (QActionEvent)."; |
|
case QEvent::ActionChanged: return "QEvent::ActionChanged - An action has been changed (QActionEvent)."; |
|
case QEvent::ActionRemoved: return "QEvent::ActionRemoved - An action has been removed (QActionEvent)."; |
|
case QEvent::ActivationChange: return "QEvent::ActivationChange - A widget's top-level window activation state has changed."; |
|
case QEvent::ApplicationActivate: return "QEvent::ApplicationActivate - This enum has been deprecated. Use ApplicationStateChange instead."; |
|
//case QEvent::ApplicationActivated: return "QEvent::ApplicationActivated - This enum has been deprecated. Use ApplicationStateChange instead."; |
|
case QEvent::ApplicationDeactivate: return "QEvent::ApplicationDeactivate - This enum has been deprecated. Use ApplicationStateChange instead."; |
|
case QEvent::ApplicationFontChange: return "QEvent::ApplicationFontChange - The default application font has changed."; |
|
case QEvent::ApplicationLayoutDirectionChange: return "QEvent::ApplicationLayoutDirectionChange - The default application layout direction has changed."; |
|
case QEvent::ApplicationPaletteChange: return "QEvent::ApplicationPaletteChange - The default application palette has changed."; |
|
case QEvent::ApplicationStateChange: return "QEvent::ApplicationStateChange - The state of the application has changed."; |
|
case QEvent::ApplicationWindowIconChange: return "QEvent::ApplicationWindowIconChange - The application's icon has changed."; |
|
case QEvent::ChildAdded: return "QEvent::ChildAdded - An object gets a child (QChildEvent)."; |
|
case QEvent::ChildPolished: return "QEvent::ChildPolished - A widget child gets polished (QChildEvent)."; |
|
case QEvent::ChildRemoved: return "QEvent::ChildRemoved - An object loses a child (QChildEvent)."; |
|
case QEvent::Clipboard: return "QEvent::Clipboard - The clipboard contents have changed (QClipboardEvent)."; |
|
case QEvent::Close: return "QEvent::Close - Widget was closed (QCloseEvent)."; |
|
case QEvent::CloseSoftwareInputPanel: return "QEvent::CloseSoftwareInputPanel - A widget wants to close the software input panel (SIP)."; |
|
case QEvent::ContentsRectChange: return "QEvent::ContentsRectChange - The margins of the widget's content rect changed."; |
|
case QEvent::ContextMenu: return "QEvent::ContextMenu - Context popup menu (QContextMenuEvent)."; |
|
case QEvent::CursorChange: return "QEvent::CursorChange - The widget's cursor has changed."; |
|
case QEvent::DeferredDelete: return "QEvent::DeferredDelete - The object will be deleted after it has cleaned up (QDeferredDeleteEvent)."; |
|
case QEvent::DragEnter: return "QEvent::DragEnter - The cursor enters a widget during a drag and drop operation (QDragEnterEvent)."; |
|
case QEvent::DragLeave: return "QEvent::DragLeave - The cursor leaves a widget during a drag and drop operation (QDragLeaveEvent)."; |
|
case QEvent::DragMove: return "QEvent::DragMove - A drag and drop operation is in progress (QDragMoveEvent)."; |
|
case QEvent::Drop: return "QEvent::Drop - A drag and drop operation is completed (QDropEvent)."; |
|
case QEvent::DynamicPropertyChange: return "QEvent::DynamicPropertyChange - A dynamic property was added, changed, or removed from the object."; |
|
case QEvent::EnabledChange: return "QEvent::EnabledChange - Widget's enabled state has changed."; |
|
case QEvent::Enter: return "QEvent::Enter - Mouse enters widget's boundaries (QEnterEvent)."; |
|
//case QEvent::EnterEditFocus: return "QEvent::EnterEditFocus - An editor widget gains focus for editing. QT_KEYPAD_NAVIGATION must be defined."; |
|
case QEvent::EnterWhatsThisMode: return "QEvent::EnterWhatsThisMode - Send to toplevel widgets when the application enters \"What's This?\" mode."; |
|
case QEvent::Expose: return "QEvent::Expose - Sent to a window when its on-screen contents are invalidated and need to be flushed from the backing store."; |
|
case QEvent::FileOpen: return "QEvent::FileOpen - File open request (QFileOpenEvent)."; |
|
case QEvent::FocusIn: return "QEvent::FocusIn - Widget or Window gains keyboard focus (QFocusEvent)."; |
|
case QEvent::FocusOut: return "QEvent::FocusOut - Widget or Window loses keyboard focus (QFocusEvent)."; |
|
case QEvent::FocusAboutToChange: return "QEvent::FocusAboutToChange - Widget or Window focus is about to change (QFocusEvent)"; |
|
case QEvent::FontChange: return "QEvent::FontChange - Widget's font has changed."; |
|
case QEvent::Gesture: return "QEvent::Gesture - A gesture was triggered (QGestureEvent)."; |
|
case QEvent::GestureOverride: return "QEvent::GestureOverride - A gesture override was triggered (QGestureEvent)."; |
|
case QEvent::GrabKeyboard: return "QEvent::GrabKeyboard - Item gains keyboard grab (QGraphicsItem only)."; |
|
case QEvent::GrabMouse: return "QEvent::GrabMouse - Item gains mouse grab (QGraphicsItem only)."; |
|
case QEvent::GraphicsSceneContextMenu: return "QEvent::GraphicsSceneContextMenu - Context popup menu over a graphics scene (QGraphicsSceneContextMenuEvent)."; |
|
case QEvent::GraphicsSceneDragEnter: return "QEvent::GraphicsSceneDragEnter - The cursor enters a graphics scene during a drag and drop operation (QGraphicsSceneDragDropEvent)."; |
|
case QEvent::GraphicsSceneDragLeave: return "QEvent::GraphicsSceneDragLeave - The cursor leaves a graphics scene during a drag and drop operation (QGraphicsSceneDragDropEvent)."; |
|
case QEvent::GraphicsSceneDragMove: return "QEvent::GraphicsSceneDragMove - A drag and drop operation is in progress over a scene (QGraphicsSceneDragDropEvent)."; |
|
case QEvent::GraphicsSceneDrop: return "QEvent::GraphicsSceneDrop - A drag and drop operation is completed over a scene (QGraphicsSceneDragDropEvent)."; |
|
case QEvent::GraphicsSceneHelp: return "QEvent::GraphicsSceneHelp - The user requests help for a graphics scene (QHelpEvent)."; |
|
case QEvent::GraphicsSceneHoverEnter: return "QEvent::GraphicsSceneHoverEnter - The mouse cursor enters a hover item in a graphics scene (QGraphicsSceneHoverEvent)."; |
|
case QEvent::GraphicsSceneHoverLeave: return "QEvent::GraphicsSceneHoverLeave - The mouse cursor leaves a hover item in a graphics scene (QGraphicsSceneHoverEvent)."; |
|
case QEvent::GraphicsSceneHoverMove: return "QEvent::GraphicsSceneHoverMove - The mouse cursor moves inside a hover item in a graphics scene (QGraphicsSceneHoverEvent)."; |
|
case QEvent::GraphicsSceneMouseDoubleClick: return "QEvent::GraphicsSceneMouseDoubleClick - Mouse press again (double click) in a graphics scene (QGraphicsSceneMouseEvent)."; |
|
case QEvent::GraphicsSceneMouseMove: return "QEvent::GraphicsSceneMouseMove - Move mouse in a graphics scene (QGraphicsSceneMouseEvent)."; |
|
case QEvent::GraphicsSceneMousePress: return "QEvent::GraphicsSceneMousePress - Mouse press in a graphics scene (QGraphicsSceneMouseEvent)."; |
|
case QEvent::GraphicsSceneMouseRelease: return "QEvent::GraphicsSceneMouseRelease - Mouse release in a graphics scene (QGraphicsSceneMouseEvent)."; |
|
case QEvent::GraphicsSceneMove: return "QEvent::GraphicsSceneMove - Widget was moved (QGraphicsSceneMoveEvent)."; |
|
case QEvent::GraphicsSceneResize: return "QEvent::GraphicsSceneResize - Widget was resized (QGraphicsSceneResizeEvent)."; |
|
case QEvent::GraphicsSceneWheel: return "QEvent::GraphicsSceneWheel - Mouse wheel rolled in a graphics scene (QGraphicsSceneWheelEvent)."; |
|
case QEvent::Hide: return "QEvent::Hide - Widget was hidden (QHideEvent)."; |
|
case QEvent::HideToParent: return "QEvent::HideToParent - A child widget has been hidden."; |
|
case QEvent::HoverEnter: return "QEvent::HoverEnter - The mouse cursor enters a hover widget (QHoverEvent)."; |
|
case QEvent::HoverLeave: return "QEvent::HoverLeave - The mouse cursor leaves a hover widget (QHoverEvent)."; |
|
case QEvent::HoverMove: return "QEvent::HoverMove - The mouse cursor moves inside a hover widget (QHoverEvent)."; |
|
case QEvent::IconDrag: return "QEvent::IconDrag - The main icon of a window has been dragged away (QIconDragEvent)."; |
|
case QEvent::IconTextChange: return "QEvent::IconTextChange - Widget's icon text has been changed."; |
|
case QEvent::InputMethod: return "QEvent::InputMethod - An input method is being used (QInputMethodEvent)."; |
|
case QEvent::InputMethodQuery: return "QEvent::InputMethodQuery - A input method query event (QInputMethodQueryEvent)"; |
|
case QEvent::KeyboardLayoutChange: return "QEvent::KeyboardLayoutChange - The keyboard layout has changed."; |
|
case QEvent::KeyPress: return "QEvent::KeyPress - Key press (QKeyEvent)."; |
|
case QEvent::KeyRelease: return "QEvent::KeyRelease - Key release (QKeyEvent)."; |
|
case QEvent::LanguageChange: return "QEvent::LanguageChange - The application translation changed."; |
|
case QEvent::LayoutDirectionChange: return "QEvent::LayoutDirectionChange - The direction of layouts changed."; |
|
case QEvent::LayoutRequest: return "QEvent::LayoutRequest - Widget layout needs to be redone."; |
|
case QEvent::Leave: return "QEvent::Leave - Mouse leaves widget's boundaries."; |
|
//case QEvent::LeaveEditFocus: return "QEvent::LeaveEditFocus - An editor widget loses focus for editing. QT_KEYPAD_NAVIGATION must be defined."; |
|
case QEvent::LeaveWhatsThisMode: return "QEvent::LeaveWhatsThisMode - Send to toplevel widgets when the application leaves \"What's This?\" mode."; |
|
case QEvent::LocaleChange: return "QEvent::LocaleChange - The system locale has changed."; |
|
case QEvent::NonClientAreaMouseButtonDblClick: return "QEvent::NonClientAreaMouseButtonDblClick - A mouse double click occurred outside the client area."; |
|
case QEvent::NonClientAreaMouseButtonPress: return "QEvent::NonClientAreaMouseButtonPress - A mouse button press occurred outside the client area."; |
|
case QEvent::NonClientAreaMouseButtonRelease: return "QEvent::NonClientAreaMouseButtonRelease - A mouse button release occurred outside the client area."; |
|
case QEvent::NonClientAreaMouseMove: return "QEvent::NonClientAreaMouseMove - A mouse move occurred outside the client area."; |
|
case QEvent::MacSizeChange: return "QEvent::MacSizeChange - The user changed his widget sizes (Mac OS X only)."; |
|
case QEvent::MetaCall: return "QEvent::MetaCall - An asynchronous method invocation via QMetaObject::invokeMethod()."; |
|
case QEvent::ModifiedChange: return "QEvent::ModifiedChange - Widgets modification state has been changed."; |
|
case QEvent::MouseButtonDblClick: return "QEvent::MouseButtonDblClick - Mouse press again (QMouseEvent)."; |
|
case QEvent::MouseButtonPress: return "QEvent::MouseButtonPress - Mouse press (QMouseEvent)."; |
|
case QEvent::MouseButtonRelease: return "QEvent::MouseButtonRelease - Mouse release (QMouseEvent)."; |
|
case QEvent::MouseMove: return "QEvent::MouseMove - Mouse move (QMouseEvent)."; |
|
case QEvent::MouseTrackingChange: return "QEvent::MouseTrackingChange - The mouse tracking state has changed."; |
|
case QEvent::Move: return "QEvent::Move - Widget's position changed (QMoveEvent)."; |
|
case QEvent::NativeGesture: return "QEvent::NativeGesture - The system has detected a gesture (QNativeGestureEvent)."; |
|
case QEvent::OrientationChange: return "QEvent::OrientationChange - The screens orientation has changes (QScreenOrientationChangeEvent)"; |
|
case QEvent::Paint: return "QEvent::Paint - Screen update necessary (QPaintEvent)."; |
|
case QEvent::PaletteChange: return "QEvent::PaletteChange - Palette of the widget changed."; |
|
case QEvent::ParentAboutToChange: return "QEvent::ParentAboutToChange - The widget parent is about to change."; |
|
case QEvent::ParentChange: return "QEvent::ParentChange - The widget parent has changed."; |
|
case QEvent::PlatformPanel: return "QEvent::PlatformPanel - A platform specific panel has been requested."; |
|
case QEvent::Polish: return "QEvent::Polish - The widget is polished."; |
|
case QEvent::PolishRequest: return "QEvent::PolishRequest - The widget should be polished."; |
|
case QEvent::QueryWhatsThis: return "QEvent::QueryWhatsThis - The widget should accept the event if it has \"What's This?\" help."; |
|
//case QEvent::ReadOnlyChange: return "QEvent::ReadOnlyChange - Widget's read-only state has changed (since Qt 5.4)."; |
|
case QEvent::RequestSoftwareInputPanel: return "QEvent::RequestSoftwareInputPanel - A widget wants to open a software input panel (SIP)."; |
|
case QEvent::Resize: return "QEvent::Resize - Widget's size changed (QResizeEvent)."; |
|
case QEvent::ScrollPrepare: return "QEvent::ScrollPrepare - The object needs to fill in its geometry information (QScrollPrepareEvent)."; |
|
case QEvent::Scroll: return "QEvent::Scroll - The object needs to scroll to the supplied position (QScrollEvent)."; |
|
case QEvent::Shortcut: return "QEvent::Shortcut - Key press in child for shortcut key handling (QShortcutEvent)."; |
|
case QEvent::ShortcutOverride: return "QEvent::ShortcutOverride - Key press in child, for overriding shortcut key handling (QKeyEvent)."; |
|
case QEvent::Show: return "QEvent::Show - Widget was shown on screen (QShowEvent)."; |
|
case QEvent::ShowToParent: return "QEvent::ShowToParent - A child widget has been shown."; |
|
case QEvent::SockAct: return "QEvent::SockAct - Socket activated, used to implement QSocketNotifier."; |
|
case QEvent::StateMachineSignal: return "QEvent::StateMachineSignal - A signal delivered to a state machine (QStateMachine::SignalEvent)."; |
|
case QEvent::StateMachineWrapped: return "QEvent::StateMachineWrapped - The event is a wrapper for, i.e., contains, another event (QStateMachine::WrappedEvent)."; |
|
case QEvent::StatusTip: return "QEvent::StatusTip - A status tip is requested (QStatusTipEvent)."; |
|
case QEvent::StyleChange: return "QEvent::StyleChange - Widget's style has been changed."; |
|
case QEvent::TabletMove: return "QEvent::TabletMove - Wacom tablet move (QTabletEvent)."; |
|
case QEvent::TabletPress: return "QEvent::TabletPress - Wacom tablet press (QTabletEvent)."; |
|
case QEvent::TabletRelease: return "QEvent::TabletRelease - Wacom tablet release (QTabletEvent)."; |
|
case QEvent::OkRequest: return "QEvent::OkRequest - Ok button in decoration pressed. Supported only for Windows CE."; |
|
case QEvent::TabletEnterProximity: return "QEvent::TabletEnterProximity - Wacom tablet enter proximity event (QTabletEvent), sent to QApplication."; |
|
case QEvent::TabletLeaveProximity: return "QEvent::TabletLeaveProximity - Wacom tablet leave proximity event (QTabletEvent), sent to QApplication."; |
|
case QEvent::ThreadChange: return "QEvent::ThreadChange - The object is moved to another thread. This is the last event sent to this object in the previous thread. See QObject::moveToThread()."; |
|
case QEvent::Timer: return "QEvent::Timer - Regular timer events (QTimerEvent)."; |
|
case QEvent::ToolBarChange: return "QEvent::ToolBarChange - The toolbar button is toggled on Mac OS X."; |
|
case QEvent::ToolTip: return "QEvent::ToolTip - A tooltip was requested (QHelpEvent)."; |
|
case QEvent::ToolTipChange: return "QEvent::ToolTipChange - The widget's tooltip has changed."; |
|
case QEvent::TouchBegin: return "QEvent::TouchBegin - Beginning of a sequence of touch-screen or track-pad events (QTouchEvent)."; |
|
case QEvent::TouchCancel: return "QEvent::TouchCancel - Cancellation of touch-event sequence (QTouchEvent)."; |
|
case QEvent::TouchEnd: return "QEvent::TouchEnd - End of touch-event sequence (QTouchEvent)."; |
|
case QEvent::TouchUpdate: return "QEvent::TouchUpdate - Touch-screen event (QTouchEvent)."; |
|
case QEvent::UngrabKeyboard: return "QEvent::UngrabKeyboard - Item loses keyboard grab (QGraphicsItem only)."; |
|
case QEvent::UngrabMouse: return "QEvent::UngrabMouse - Item loses mouse grab (QGraphicsItem only)."; |
|
case QEvent::UpdateLater: return "QEvent::UpdateLater - The widget should be queued to be repainted at a later time."; |
|
case QEvent::UpdateRequest: return "QEvent::UpdateRequest - The widget should be repainted."; |
|
case QEvent::WhatsThis: return "QEvent::WhatsThis - The widget should reveal \"What's This?\" help (QHelpEvent)."; |
|
case QEvent::WhatsThisClicked: return "QEvent::WhatsThisClicked - A link in a widget's \"What's This?\" help was clicked."; |
|
case QEvent::Wheel: return "QEvent::Wheel - Mouse wheel rolled (QWheelEvent)."; |
|
case QEvent::WinEventAct: return "QEvent::WinEventAct - A Windows-specific activation event has occurred."; |
|
case QEvent::WindowActivate: return "QEvent::WindowActivate - Window was activated."; |
|
case QEvent::WindowBlocked: return "QEvent::WindowBlocked - The window is blocked by a modal dialog."; |
|
case QEvent::WindowDeactivate: return "QEvent::WindowDeactivate - Window was deactivated."; |
|
case QEvent::WindowIconChange: return "QEvent::WindowIconChange - The window's icon has changed."; |
|
case QEvent::WindowStateChange: return "QEvent::WindowStateChange - The window's state (minimized, maximized or full-screen) has changed (QWindowStateChangeEvent)."; |
|
case QEvent::WindowTitleChange: return "QEvent::WindowTitleChange - The window title has changed."; |
|
case QEvent::WindowUnblocked: return "QEvent::WindowUnblocked - The window is unblocked after a modal dialog exited."; |
|
case QEvent::WinIdChange: return "QEvent::WinIdChange - The window system identifer for this native widget has changed."; |
|
case QEvent::ZOrderChange: return "QEvent::ZOrderChange - The widget's z-order has changed. This event is never sent to top level windows."; |
|
default: |
|
return QString("%1").arg(t); |
|
} |
|
} |
|
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 |
|
Script _setupScript; |
|
}; |
|
|
|
#endif // TESTGUI_HXX
|
|
|