diff --git a/src/commands.hxx b/src/commands.hxx index eed306f..4f2f20e 100644 --- a/src/commands.hxx +++ b/src/commands.hxx @@ -30,6 +30,45 @@ #include #include +namespace std { + template 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()+" \n \n " + tag()+" []\n \n " "\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 parse(Script*, QString args, QStringList& in, int) { std::shared_ptr 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(find(frame, script->replacevars(_selector))); - if (element.isNull()) - throw ElementNotFound(script->replacevars(_selector)); + 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()+" " + tag()+" [] " "\n\n" - "Click on the specified element"; + "Click on the specified element. Either you explicitely specify a click" + " type, such as or , 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 parse(Script*, QString args, QStringList&, int) { std::shared_ptr 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 _clicktype; }; class Set: public Command { diff --git a/src/testgui.hxx b/src/testgui.hxx index db1af35..bde9228 100644 --- a/src/testgui.hxx +++ b/src/testgui.hxx @@ -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)