diff --git a/src/calibre/web/jsbrowser/browser.py b/src/calibre/web/jsbrowser/browser.py index 5d569a3fac..c4341171ea 100644 --- a/src/calibre/web/jsbrowser/browser.py +++ b/src/calibre/web/jsbrowser/browser.py @@ -130,6 +130,7 @@ class NetworkAccessManager(QNetworkAccessManager): # {{{ def __init__(self, log, use_disk_cache=True, parent=None): QNetworkAccessManager.__init__(self, parent) + self.reply_count = 0 self.log = log if use_disk_cache: self.cache = QNetworkDiskCache(self) @@ -170,6 +171,7 @@ class NetworkAccessManager(QNetworkAccessManager): # {{{ def on_finished(self, reply): reply_url = unicode(reply.url().toString()) + self.reply_count += 1 if reply.error(): self.log.warn("Reply error: %s - %d (%s)" % @@ -286,6 +288,17 @@ class Browser(QObject, FormsMixin): return lw.loaded_ok + def _wait_for_replies(self, reply_count, timeout): + final_time = time.time() + timeout + loop = QEventLoop(self) + while (time.time() < final_time and self.nam.reply_count < + reply_count): + loop.processEvents() + time.sleep(0.1) + if self.nam.reply_count < reply_count: + raise Timeout('Waiting for replies took longer than %d seconds' % + timeout) + def visit(self, url, timeout=30.0): ''' Open the page specified in URL and wait for it to complete loading. @@ -310,6 +323,7 @@ class Browser(QObject, FormsMixin): :para ajax_replies: Number of replies to wait for after clicking a link that triggers some AJAX interaction ''' + initial_count = self.nam.reply_count js = ''' var e = document.createEvent('MouseEvents'); e.initEvent( 'click', true, true ); @@ -317,7 +331,8 @@ class Browser(QObject, FormsMixin): ''' qwe.evaluateJavaScript(js) if ajax_replies > 0: - raise NotImplementedError('AJAX clicking not implemented') + reply_count = initial_count + ajax_replies + self._wait_for_replies(reply_count, timeout) elif wait_for_load and not self._wait_for_load(timeout): raise LoadError('Clicking resulted in a failed load') diff --git a/src/calibre/web/jsbrowser/forms.py b/src/calibre/web/jsbrowser/forms.py index 8a837a1fc2..64c652716c 100644 --- a/src/calibre/web/jsbrowser/forms.py +++ b/src/calibre/web/jsbrowser/forms.py @@ -229,3 +229,15 @@ class FormsMixin(object): self.click(sc.qwe, wait_for_load=wait_for_load, ajax_replies=ajax_replies, timeout=timeout) + def ajax_submit(self, submit_control_selector=None, + num_of_replies=1, timeout=30.0): + ''' + Submit the current form. This method is meant for those forms that + use AJAX rather than a plain submit. It will block until the specified + number of responses are returned from the server after the submit + button is clicked. + ''' + self.submit(submit_control_selector=submit_control_selector, + wait_for_load=False, ajax_replies=num_of_replies, + timeout=timeout) + diff --git a/src/calibre/web/jsbrowser/test.py b/src/calibre/web/jsbrowser/test.py index fbafad3f0c..3c9b98fbcf 100644 --- a/src/calibre/web/jsbrowser/test.py +++ b/src/calibre/web/jsbrowser/test.py @@ -23,6 +23,24 @@ class Server(object): return ''' JS Browser test + + +

Test controls

@@ -40,6 +58,12 @@ class Server(object):
+
+

Test AJAX submit

+
+ +
+ ''' @@ -55,6 +79,12 @@ class Server(object): cherrypy.response.headers['Content-Type'] = 'image/png' return I('next.png', data=True) + @cherrypy.expose + def jquery(self): + cherrypy.response.headers['Content-Type'] = 'text/javascript' + return P('content_server/jquery.js', data=True) + + class Test(unittest.TestCase): @classmethod @@ -121,6 +151,15 @@ class Test(unittest.TestCase): self.browser.submit() self.assertEqual(self.server.form_data['text'], 'Image Test') + def test_ajax_submit(self): + 'Test AJAX based form submission' + self.assertEqual(self.browser.visit('http://127.0.0.1:%d'%self.port), + True) + f = self.browser.select_form('#ajax_test') + f['text'] = 'Changed' + self.browser.ajax_submit() + self.assertEqual(self.server.form_data['text'], 'Changed') + def tests(): return unittest.TestLoader().loadTestsFromTestCase(Test)