diff --git a/addons/web/tests/__init__.py b/addons/web/tests/__init__.py index 8c20aed1ce5..beb9e90eb76 100644 --- a/addons/web/tests/__init__.py +++ b/addons/web/tests/__init__.py @@ -2,4 +2,3 @@ import test_js import test_menu import test_serving_base -import test_ui diff --git a/addons/web/tests/qunitsuite/README.rst b/addons/web/tests/qunitsuite/README.rst deleted file mode 100644 index f1cfb96a508..00000000000 --- a/addons/web/tests/qunitsuite/README.rst +++ /dev/null @@ -1,116 +0,0 @@ -QUnitSuite is a ``unittest.TestSuite`` able to run QUnit_ test suites -within the normal unittest process, through PhantomJS_. - -QUnitSuite is built upon `Ben Alman`_'s work of for the interfacing -between PhantomJS_ and the host/reporting code: the shims and the -PhantomJS_ configuration files are those of grunt_'s ``qunit`` task. - -Why ---- - -You're a Python shop or developer, you have tools and tests built -around unittest (or compatible with unittests) and your testing -pipeline is predicated upon that, you're doing web development of some -sort these days (as so many are) and you'd like to do some testing of -your web stuff. - -But you don't really want to redo your whole testing stack just for -that. - -QUnitSuite simply grafts QUnit_-based tests, run in PhantomJS_, in -your existing ``unittest``-based architecture. - -What ----- - -QUnitSuite currently provides a single object as part of its API: -``qunitsuite.QUnitSuite(testfile[, timeout])``. - -This produces a ``unittest.TestSuite`` suitable for all the usual -stuff (running it, and giving it to an other test suite which will run -it, that is). - -``testfile`` is the HTML file bootstrapping your qunit tests, as would -usually be accessed via a browser. It can be either a local -(``file:``) url, or an HTTP one. As long as a regular browser can open -and execute it, PhantomJS_ will manage. - -``timeout`` is a check passed to the PhantomJS_ runner: if the runner -produces no information for longer than ``timeout`` milliseconds, the -run will be cancelled and a test error will be generated. This -situation usually means either your ``testfile`` is not a qunit test -file, qunit is not running or qunit's runner was stopped (for an async -test) and never restarted. - -The default value is very conservative, most tests should run -correctly with lower timeouts (especially if all tests are -synchronous). - -How ---- - -``unittest``'s autodiscovery protocol does not directly work with test -suites (it looks for test cases). If you want autodiscovery to work -correctly, you will have to use the ``load_tests`` protocol:: - - # in a testing module - def load_tests(loader, tests, pattern): - tests.addTest(QUnitSuite(qunit_test_path.html)) - return tests - -outside of that specific case, you can use a ``QUnitSuite`` as a -standard ``TestSuite`` instance, running it, adding it to an other -suite or passing it to a ``TestRunner`` - -Complaints and Grievances -------------------------- - -Speed -~~~~~ - -Starting up a phantomjs instance and running a suite turns out to have -a rather high overhead, on the order of a second on this machine -(2.4GHz, 8GB RAM and an SSD). - -As each ``QUnitSuite`` currently creates its own phantomjs instance, -it's probably a good idea to create bigger suites (put many modules & -tests in the same QUnit html file, which doesn't preclude splitting -them across multiple js files). - -Hacks -~~~~~ - -QUnitSuite contains a pretty big hack which may or may not cause -problem depending on your exact setup: in case of case failure or -error, ``unittest.TestResult`` formats the error traceback provided -alongside the test object. This goes through Python's -traceback-formatting code and there are no hooks there. - -One could expect to use a custom ``TestResult``, but for test suites -the ``TestResult`` instance must be provided by the caller, so there -is no direct hook onto it. - -This leaves three options: - -* Create a custom ``TestResult`` class and require that it be the one - provided to the test suite. This requires altered work flows, - customization of the test runner and (as far as I know) isn't - available through Python 2.7's autodiscovery. It's the cleanest - option but completely fails on practicality. - -* Create a custom ``TestResult`` which directly alters the original - result's ``errors`` and ``failures`` attributes as they're part of - the testrunner API. This would work but may put custom results in a - strange state and break e.g. unittest2's ``@failfast``. - -* Lastly, monkeypatch the undocumented and implementation detail - ``_exc_info_to_string`` on the provided ``result``. This is the - route taken, at least for now. - -.. _QUnit: http://qunitjs.com/ - -.. _PhantomJS: http://phantomjs.org/ - -.. _Ben Alman: http://benalman.com/ - -.. _grunt: http://gruntjs.com/ diff --git a/addons/web/tests/qunitsuite/__init__.py b/addons/web/tests/qunitsuite/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/addons/web/tests/qunitsuite/grunt/bootstrap.js b/addons/web/tests/qunitsuite/grunt/bootstrap.js deleted file mode 100644 index 3d5e38912ca..00000000000 --- a/addons/web/tests/qunitsuite/grunt/bootstrap.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * grunt - * http://gruntjs.com/ - * - * Copyright (c) 2012 "Cowboy" Ben Alman - * Licensed under the MIT license. - * http://benalman.com/about/license/ - */ - -/*global phantom:true*/ - -'use strict'; - -var fs = require('fs'); - -// The page .html file to load. -var url = phantom.args[0]; -// Extra, optionally overridable stuff. -var options = JSON.parse(phantom.args[1] || {}); - -// Default options. -if (!options.timeout) { options.timeout = 5000; } - -// Keep track of the last time a client message was sent. -var last = new Date(); - -// Messages are sent to the parent by appending them to the tempfile. -var sendMessage = function(arg) { - var args = Array.isArray(arg) ? arg : [].slice.call(arguments); - last = new Date(); - console.log(JSON.stringify(args)); -}; - -// This allows grunt to abort if the PhantomJS version isn't adequate. -sendMessage('private', 'version', phantom.version); - -// Abort if the page doesn't send any messages for a while. -setInterval(function() { - if (new Date() - last > options.timeout) { - sendMessage('fail.timeout'); - phantom.exit(); - } -}, 100); - -// Create a new page. -var page = require('webpage').create(); - -// The client page must send its messages via alert(jsonstring). -page.onAlert = function(args) { - sendMessage(JSON.parse(args)); -}; - -// Keep track if the client-side helper script already has been injected. -var injected; -page.onUrlChanged = function(newUrl) { - injected = false; - sendMessage('onUrlChanged', newUrl); -}; - -// Relay console logging messages. -page.onConsoleMessage = function(message) { - sendMessage('console', message); -}; - -// For debugging. -page.onResourceRequested = function(request) { - sendMessage('onResourceRequested', request.url); -}; - -page.onResourceReceived = function(request) { - if (request.stage === 'end') { - sendMessage('onResourceReceived', request.url); - } -}; - -// Run when the page has finished loading. -page.onLoadFinished = function(status) { - // The window has loaded. - sendMessage('onLoadFinished', status); - if (status === 'success') { - if (options.inject && !injected) { - // Inject client-side helper script, but only if it has not yet been - // injected. - sendMessage('inject', options.inject); - page.injectJs(options.inject); - } - } else { - // File loading failure. - sendMessage('fail.load', url); - phantom.exit(); - } -}; - -// Actually load url. -page.open(url); diff --git a/addons/web/tests/qunitsuite/grunt/license b/addons/web/tests/qunitsuite/grunt/license deleted file mode 100644 index 90c336c39d3..00000000000 --- a/addons/web/tests/qunitsuite/grunt/license +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012 "Cowboy" Ben Alman - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/addons/web/tests/qunitsuite/grunt/phantomjs.json b/addons/web/tests/qunitsuite/grunt/phantomjs.json deleted file mode 100644 index 9e26dfeeb6e..00000000000 --- a/addons/web/tests/qunitsuite/grunt/phantomjs.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/addons/web/tests/qunitsuite/grunt/qunit-phantomjs-bridge.js b/addons/web/tests/qunitsuite/grunt/qunit-phantomjs-bridge.js deleted file mode 100644 index 032fe8a3aa0..00000000000 --- a/addons/web/tests/qunitsuite/grunt/qunit-phantomjs-bridge.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * grunt - * http://gruntjs.com/ - * - * Copyright (c) 2012 "Cowboy" Ben Alman - * Licensed under the MIT license. - * http://benalman.com/about/license/ - */ - -/*global QUnit:true, alert:true*/ - -'use strict'; - -// Don't re-order tests. -QUnit.config.reorder = false; -// Run tests serially, not in parallel. -QUnit.config.autorun = false; - -// Send messages to the parent PhantomJS process via alert! Good times!! -function sendMessage() { - var args = [].slice.call(arguments); - alert(JSON.stringify(args)); -} - -// These methods connect QUnit to PhantomJS. -QUnit.log(function(obj) { - // What is this I don’t even - if (obj.message === '[object Object], undefined:undefined') { return; } - // Parse some stuff before sending it. - var actual = QUnit.jsDump.parse(obj.actual); - var expected = QUnit.jsDump.parse(obj.expected); - // Send it. - sendMessage('qunit.log', obj.result, actual, expected, obj.message, obj.source); -}); - -QUnit.testStart(function(obj) { - sendMessage('qunit.testStart', obj.name); -}); - -QUnit.testDone(function(obj) { - sendMessage('qunit.testDone', obj.name, obj.failed, obj.passed, obj.total); -}); - -QUnit.moduleStart(function(obj) { - sendMessage('qunit.moduleStart', obj.name); -}); - -QUnit.moduleDone(function(obj) { - sendMessage('qunit.moduleDone', obj.name, obj.failed, obj.passed, obj.total); -}); - -QUnit.begin(function() { - sendMessage('qunit.begin'); -}); - -QUnit.done(function(obj) { - sendMessage('qunit.done', obj.failed, obj.passed, obj.total, obj.runtime); -}); - -// PhantomJS (up to and including 1.7) uses a version of webkit so old -// it does not have Function.prototype.bind: -// http://code.google.com/p/phantomjs/issues/detail?id=522 - -// Use moz polyfill: -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} diff --git a/addons/web/tests/qunitsuite/suite.py b/addons/web/tests/qunitsuite/suite.py deleted file mode 100644 index 54d2e18e36e..00000000000 --- a/addons/web/tests/qunitsuite/suite.py +++ /dev/null @@ -1,136 +0,0 @@ -import json -import subprocess -import unittest -import os -import time - -ROOT = os.path.join(os.path.dirname(__file__), 'grunt') - -__all__ = ['QUnitSuite'] - -def _exc_info_to_string(err, test): - return err - -class QUnitTest(unittest.TestCase): - def __init__(self, module, name): - self.module = module - self.name = name - self.failed = False - def shortDescription(self): - return None - def __repr__(self): - return '' % (self.module, self.name) - def __str__(self): - return '%s: %s' % (self.module, self.name) - -class QUnitSuite(unittest.TestSuite): - def __init__(self, qunitfile, timeout=5000): - super(QUnitSuite, self).__init__() - self.testfile = qunitfile - self.timeout = timeout - self._module = None - self._test = None - - def run(self, result): - try: - subprocess.call(['phantomjs', '-v'], - stdout=open(os.devnull, 'w'), - stderr=subprocess.STDOUT) - except OSError: - test = QUnitTest('phantomjs', 'javascript tests') - result.startTest(test) - result.startTest(test) - result.addSkip(test , "phantomjs command not found") - result.stopTest(test) - return - - result._exc_info_to_string = _exc_info_to_string - try: - self._run(result) - finally: - del result._exc_info_to_string - - def _run(self, result): - phantom = subprocess.Popen([ - 'phantomjs', - '--config=%s' % os.path.join(ROOT, 'phantomjs.json'), - os.path.join(ROOT, 'bootstrap.js'), self.testfile, - json.dumps({ - 'timeout': self.timeout, - 'inject': os.path.join(ROOT, 'qunit-phantomjs-bridge.js') - }) - ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - try: - while True: - line = phantom.stdout.readline() - if line: - if self.process(line, result): - break - else: - time.sleep(0.1) - finally: - # If the phantomjs process hasn't quit, kill it - if phantom.poll() is None: - phantom.terminate() - - def process(self, line, result): - try: - args = json.loads(line) - except ValueError: # phantomjs stderr - if 'CoreText' not in line: - print line - return False - event_name = args[0] - - if event_name == 'qunit.done': - return True - elif event_name == 'fail.load': - self.add_error(result, "PhantomJS unable to load %s" % args[1]) - return True - elif event_name == 'fail.timeout': - self.add_error(result, "PhantomJS timed out, possibly due to a" - " missing QUnit start() call") - return True - - elif event_name == 'qunit.moduleStart': - self._module = args[1].encode('utf-8') if args[1] else '' - elif event_name == 'qunit.moduleStop': - self._test = None - self._module = None - elif event_name == 'qunit.testStart': - self._test = QUnitTest(self._module, args[1].encode('utf-8')) - result.startTest(self._test) - elif event_name == 'qunit.testDone': - if not self._test.failed: - result.addSuccess(self._test) - result.stopTest(self._test) - self._test = None - elif event_name == 'qunit.log': - if args[1]: - return False - - self._test.failed = True - result.addFailure( - self._test, self.failure_to_str(*args[2:])) - elif event_name == 'console': - print args[1] - - return False - - def add_error(self, result, s): - test = QUnitTest('phantomjs', 'startup') - result.startTest(test) - result.addError(test, s) - result.stopTest(test) - - def failure_to_str(self, actual, expected, message, source): - if message or actual == expected: - formatted = str(message or '') - else: - formatted = "%s != %s" % (actual, expected) - - if source: - formatted += '\n\n' + source - - return formatted diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py index 0bab1de96a5..496d7e283bc 100644 --- a/addons/web/tests/test_js.py +++ b/addons/web/tests/test_js.py @@ -1,24 +1,6 @@ -import urllib -import urlparse -from openerp import sql_db, tools -from qunitsuite.suite import QUnitSuite +import openerp -class WebSuite(QUnitSuite): - def __init__(self, module): - url = urlparse.urlunsplit([ - 'http', - 'localhost:{port}'.format(port=tools.config['xmlrpc_port']), - '/web/tests', - urllib.urlencode({ - 'mod': module, - 'source': tools.config['db_name'], - 'supadmin': tools.config['admin_passwd'], - 'password': 'admin', - }), - '' - ]) - super(WebSuite, self).__init__(url, 50000) +class WebSuite(openerp.tests.HttpCase): + def test_01_js(self): + self.phantom_js('/web/tests?mod=web',"","", login='admin') -def load_tests(loader, standard_tests, _): - standard_tests.addTest(WebSuite('web')) - return standard_tests diff --git a/addons/web_hello/__init__.py b/addons/web_hello/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/addons/web_hello/__openerp__.py b/addons/web_hello/__openerp__.py deleted file mode 100644 index 5d8a4baf597..00000000000 --- a/addons/web_hello/__openerp__.py +++ /dev/null @@ -1,15 +0,0 @@ -{ - 'name': 'Hello', - 'category': 'Hidden', - 'description':""" -OpenERP Web example module. -=========================== - -""", - 'version': '2.0', - 'depends': [], - 'js': ['static/*/*.js', 'static/*/js/*.js'], - 'css': [], - 'auto_install': False, - 'web_preload': False, -} diff --git a/addons/web_hello/static/openerp/base_hello.js b/addons/web_hello/static/openerp/base_hello.js deleted file mode 100644 index 2043af6d4ea..00000000000 --- a/addons/web_hello/static/openerp/base_hello.js +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------- - * OpenERP base_hello (Example module) - *---------------------------------------------------------*/ - -openerp.web_hello = function(instance) { - -instance.web.SearchView = instance.web.SearchView.extend({ - init:function() { - this._super.apply(this,arguments); - this.on('search_data', this, function(){console.log('hello');}); - } -}); - -}; - -// vim:et fdc=0 fdl=0: diff --git a/addons/web_tests/__init__.py b/addons/web_tests/__init__.py index 40a96afc6ff..8757cd322f3 100644 --- a/addons/web_tests/__init__.py +++ b/addons/web_tests/__init__.py @@ -1 +1,2 @@ # -*- coding: utf-8 -*- +import tests diff --git a/addons/web_tests/__openerp__.py b/addons/web_tests/__openerp__.py index b34f96f78b2..bcbcbe9329f 100644 --- a/addons/web_tests/__openerp__.py +++ b/addons/web_tests/__openerp__.py @@ -7,7 +7,7 @@ OpenERP Web test suite. """, 'version': '2.0', - 'depends': [], + 'depends': ['web', 'web_kanban'], 'js': ['static/src/js/*.js'], 'css': ['static/src/css/*.css'], 'auto_install': True, diff --git a/addons/web_tests/tests/__init__.py b/addons/web_tests/tests/__init__.py new file mode 100644 index 00000000000..7879fd661db --- /dev/null +++ b/addons/web_tests/tests/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +import test_ui diff --git a/addons/web/tests/test_ui.py b/addons/web_tests/tests/test_ui.py similarity index 100% rename from addons/web/tests/test_ui.py rename to addons/web_tests/tests/test_ui.py diff --git a/addons/web/tests/test_ui_hello.js b/addons/web_tests/tests/test_ui_hello.js similarity index 100% rename from addons/web/tests/test_ui_hello.js rename to addons/web_tests/tests/test_ui_hello.js diff --git a/addons/web/tests/test_ui_load.js b/addons/web_tests/tests/test_ui_load.js similarity index 100% rename from addons/web/tests/test_ui_load.js rename to addons/web_tests/tests/test_ui_load.js diff --git a/addons/web_tests_demo/tests/test_js.py b/addons/web_tests_demo/tests/test_js.py deleted file mode 100644 index fb97176bad2..00000000000 --- a/addons/web_tests_demo/tests/test_js.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -from openerp.addons.web.tests.test_js import WebSuite - -def load_tests(loader, standard_tests, _): - standard_tests.addTest(WebSuite('web_tests_demo')) - return standard_tests