click has an optional clicktype; do has an optional selector; better detection and handling of moo-tool (joomla3) elements
This commit is contained in:
@@ -30,6 +30,45 @@
|
||||
#include <cassert>
|
||||
#include <xml-cxx/xml.hxx>
|
||||
|
||||
namespace std {
|
||||
template<typename T> class optional {
|
||||
private:
|
||||
T* _opt;
|
||||
bool _set;
|
||||
public:
|
||||
optional():
|
||||
_opt(0), _set(false) {
|
||||
}
|
||||
optional(const T& other):
|
||||
_opt(new T(other)), _set(true) {
|
||||
}
|
||||
~optional() {
|
||||
if (_set) delete _opt;
|
||||
}
|
||||
optional& operator=(const T& other) {
|
||||
if (_set) delete _opt;
|
||||
_set = true;
|
||||
_opt = new T(other);
|
||||
return *this;
|
||||
}
|
||||
T* operator->() {
|
||||
return _opt;
|
||||
}
|
||||
T& operator*() {
|
||||
return *_opt;
|
||||
}
|
||||
const T* operator->() const {
|
||||
return _opt;
|
||||
}
|
||||
const T& operator*() const {
|
||||
return *_opt;
|
||||
}
|
||||
operator bool() const {
|
||||
return _set;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Script;
|
||||
class Command;
|
||||
class Function;
|
||||
@@ -878,7 +917,7 @@ class Do: public Command {
|
||||
}
|
||||
QString description() const {
|
||||
return
|
||||
tag()+" <selector>\n <javascript-line1>\n <javascript-line2>"
|
||||
tag()+" [<selector>]\n <javascript-line1>\n <javascript-line2>"
|
||||
"\n\n"
|
||||
"Execute JavaScript on a CSS selected object. The object is the first "
|
||||
"object in the DOM tree that matches the given CSS selector. You can "
|
||||
@@ -892,15 +931,18 @@ class Do: public Command {
|
||||
std::shared_ptr<Command> parse(Script*, QString args,
|
||||
QStringList& in, int) {
|
||||
std::shared_ptr<Do> cmd(new Do());
|
||||
cmd->_selector = args;
|
||||
cmd->_selector = args.trimmed();
|
||||
cmd->_javascript = subCommandBlock(in).join("\n");
|
||||
return cmd;
|
||||
}
|
||||
bool execute(Script* script, QWebFrame* frame) {
|
||||
Logger log(this, script);
|
||||
QWebElement element(frame->documentElement());
|
||||
if (_selector.size()) {
|
||||
QWebElement element(find(frame, script->replacevars(_selector)));
|
||||
if (element.isNull())
|
||||
throw ElementNotFound(script->replacevars(_selector));
|
||||
}
|
||||
_result =
|
||||
element.evaluateJavaScript(script->replacevars(_javascript)).toString();
|
||||
return true;
|
||||
@@ -1454,32 +1496,43 @@ class Click: public Command {
|
||||
}
|
||||
QString description() const {
|
||||
return
|
||||
tag()+" <selector>"
|
||||
tag()+" [<clicktype>] <selector>"
|
||||
"\n\n"
|
||||
"Click on the specified element";
|
||||
"Click on the specified element. Either you explicitely specify a click"
|
||||
" type, such as <realmouse> or <javascript>, or the previously set or"
|
||||
" the default clicktype is used.";
|
||||
}
|
||||
QString command() const {
|
||||
return tag()+" "+_selector;
|
||||
return tag()+(_clicktype
|
||||
?(*_clicktype==Script::REAL_MOUSE_CLICK?" realmouse"
|
||||
:*_clicktype==Script::JAVASCRIPT_CLICK?" javascript":"")
|
||||
:"")+" "+_selector;
|
||||
}
|
||||
std::shared_ptr<Command> parse(Script*, QString args, QStringList&, int) {
|
||||
std::shared_ptr<Click> cmd(new Click());
|
||||
cmd->_selector = args;
|
||||
if (args.trimmed().contains(QRegularExpression("^realmouse ")))
|
||||
cmd->_clicktype = Script::REAL_MOUSE_CLICK;
|
||||
else if (args.trimmed().contains(QRegularExpression("^javascript ")))
|
||||
cmd->_clicktype = Script::JAVASCRIPT_CLICK;
|
||||
cmd->_selector =
|
||||
args.remove(QRegularExpression("^ *(realmouse|javascript) +"));
|
||||
return cmd;
|
||||
}
|
||||
bool execute(Script* script, QWebFrame* frame) {
|
||||
Logger log(this, script);
|
||||
switch (script->clicktype()) {
|
||||
QString clicktarget(script->replacevars(_selector));
|
||||
switch (_clicktype ? *_clicktype : script->clicktype()) {
|
||||
case Script::REAL_MOUSE_CLICK: {
|
||||
log("Script::REAL_MOUSE_CLICK");
|
||||
realMouseClick(frame, script->replacevars(_selector));
|
||||
realMouseClick(frame, clicktarget);
|
||||
break;
|
||||
}
|
||||
case Script::JAVASCRIPT_CLICK:
|
||||
default: {
|
||||
log("Script::JAVASCRIPT_CLICK");
|
||||
QWebElement element(find(frame, script->replacevars(_selector)));
|
||||
QWebElement element(find(frame, clicktarget));
|
||||
if (element.isNull())
|
||||
throw ElementNotFound(script->replacevars(_selector));
|
||||
throw ElementNotFound(clicktarget);
|
||||
_result = element.evaluateJavaScript("this.click();").toString();
|
||||
break;
|
||||
}
|
||||
@@ -1488,6 +1541,7 @@ class Click: public Command {
|
||||
}
|
||||
private:
|
||||
QString _selector;
|
||||
std::optional<Script::ClickType> _clicktype;
|
||||
};
|
||||
|
||||
class Set: public Command {
|
||||
|
@@ -113,6 +113,8 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
}
|
||||
void on__run_clicked() {
|
||||
bool oldRecordState(_record->isChecked());
|
||||
_record->setChecked(false);
|
||||
_record->setEnabled(false);
|
||||
_run->setEnabled(false);
|
||||
try {
|
||||
Script script;
|
||||
@@ -134,6 +136,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
.arg(x.what()));
|
||||
}
|
||||
_run->setEnabled(true);
|
||||
_record->setEnabled(true);
|
||||
_record->setChecked(oldRecordState);
|
||||
}
|
||||
void on__focused_clicked() {
|
||||
@@ -325,26 +328,9 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
_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"));
|
||||
QString selected(selector(element));
|
||||
if (handleMooTools(_lastFocused)) {
|
||||
// handled in handleMooTools
|
||||
} else if (_lastFocused.tagName()=="SELECT") {
|
||||
// click on a select results in a value change
|
||||
// find all selected options ...
|
||||
@@ -356,15 +342,14 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
v += value(option);
|
||||
}
|
||||
setValue(selected, v);
|
||||
} else {
|
||||
appendCommand("click "+map(selected));
|
||||
}
|
||||
if (_lastFocused.tagName()=="TEXTAREA" ||
|
||||
} else 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 "+map(selected));
|
||||
}
|
||||
} else {
|
||||
appendCommand("# click, but where?");
|
||||
@@ -531,7 +516,7 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
void javascript(const QString& selector, QString code) {
|
||||
if (_record->isChecked())
|
||||
appendCommand("do "+map(selector)+"\n "
|
||||
+map(code).replace("\n", "\\n"));
|
||||
+map(code).replace("\n", "\n "));
|
||||
}
|
||||
void cleanup(const QString& selector) {
|
||||
QString text(_testscript->toPlainText());
|
||||
@@ -565,6 +550,53 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
.join("', '")+"'"));
|
||||
}
|
||||
}
|
||||
bool handleMooTools(QWebElement element) {
|
||||
QString selected(selector(element));
|
||||
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 realmouse "+map(mooCombo.captured(1)+">a"));
|
||||
appendCommand("sleep "+map("1"));
|
||||
return true;
|
||||
} else if (mooComboItem.hasMatch()) {
|
||||
// special treatment for item in moo tools combobox
|
||||
appendCommand
|
||||
("click realmouse "+map("li.active-result[data-option-array-index=\""
|
||||
+element.attribute("data-option-array-index")
|
||||
+"\"]"));
|
||||
appendCommand("sleep "+map("1"));
|
||||
return true;
|
||||
} else if (element.tagName()=="INPUT") {
|
||||
for (QWebElement e(element); !(e=e.parent()).isNull();)
|
||||
if (e.hasClass("chzn-container")) {
|
||||
_lastFocused = e;
|
||||
appendCommand("# moo tools combobox with search field");
|
||||
appendCommand("# first set all elements visible");
|
||||
javascript(map(selector(e)+" ul"),
|
||||
"this.style.height = \"auto\";\n"
|
||||
"this.style.maxHeight = \"auto\";\n"
|
||||
"this.style.overflow = \"visible\"");
|
||||
appendCommand("sleep "+map("1"));
|
||||
appendCommand("# moo tools open combobox");
|
||||
appendCommand("click realmouse "+map(selector(e)));
|
||||
return true;
|
||||
}
|
||||
} else if (element.hasAttribute("id") && // is it a selected option?
|
||||
element.attribute("id").contains("_chzn_o_")) {
|
||||
appendCommand("# moo tools select item");
|
||||
appendCommand("click realmouse "+map(selector(element)));
|
||||
_lastFocused = element;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
QString execute(const QString& selector, const QString& code) {
|
||||
javascript(selector, code);
|
||||
return _web->page()->mainFrame()->documentElement().findFirst(selector)
|
||||
|
Reference in New Issue
Block a user