diff --git a/addons/web/static/src/js/test_support.js b/addons/web/static/src/js/test_support.js
new file mode 100644
index 00000000000..5fcfb94c6a2
--- /dev/null
+++ b/addons/web/static/src/js/test_support.js
@@ -0,0 +1,64 @@
+openerp.test_support = {
+ setup_connection: function (connection) {
+ var origin = location.protocol+"//"+location.host;
+ _.extend(connection, {
+ origin: origin,
+ prefix: origin,
+ server: origin, // keep chs happy
+ //openerp.web.qweb.default_dict['_s'] = this.origin;
+ rpc_function: connection.rpc_json,
+ session_id: false,
+ uid: false,
+ username: false,
+ user_context: {},
+ db: false,
+ openerp_entreprise: false,
+// this.module_list = openerp._modules.slice();
+// this.module_loaded = {};
+// _(this.module_list).each(function (mod) {
+// self.module_loaded[mod] = true;
+// });
+ context: {},
+ shortcuts: [],
+ active_id: null
+ });
+ return connection.session_reload();
+ },
+ module: function (title, tested_core, fn) {
+ var conf = QUnit.config.openerp = {};
+ QUnit.module(title, {
+ setup: function () {
+ QUnit.stop();
+ var oe = conf.openerp = window.openerp.init();
+ window.openerp.web[tested_core](oe);
+ openerp.test_support.setup_connection(oe.connection)
+ .always(QUnit.start)
+ .then(function () {
+ conf.openerp = oe;
+ }, function (e) {
+ QUnit.test(title, function () {
+ console.error(e);
+ QUnit.ok(false, 'Could not obtain a session:' + e.debug);
+ });
+ });
+ }
+ });
+ },
+ test: function (title, fn) {
+ var conf = QUnit.config.openerp;
+ QUnit.test(title, function () {
+ QUnit.stop();
+ fn(conf.openerp);
+ });
+ },
+ expect: function (promise, fn) {
+ promise.always(QUnit.start)
+ .done(function () { QUnit.ok(false, 'RPC requests should not succeed'); })
+ .fail(function (e) {
+ if (e.code !== 200) {
+ QUnit.equal(e.code, 200, 'Testing connector should raise RPC faults');
+ }
+ fn(e.data.fault_code);
+ })
+ }
+};
diff --git a/addons/web/static/test/fulltest.html b/addons/web/static/test/fulltest.html
new file mode 100644
index 00000000000..f27adfa7df9
--- /dev/null
+++ b/addons/web/static/test/fulltest.html
@@ -0,0 +1,49 @@
+
+
+
+
+ OpenERP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/web/static/test/fulltest/dataset.js b/addons/web/static/test/fulltest/dataset.js
new file mode 100644
index 00000000000..4b948261a3b
--- /dev/null
+++ b/addons/web/static/test/fulltest/dataset.js
@@ -0,0 +1,11 @@
+$(document).ready(function () {
+ var t = window.openerp.test_support;
+
+ t.module('check', 'data');
+ t.test('check1', function (openerp) {
+ var ds = new openerp.web.DataSet({session: openerp.connection}, 'res.users', {});
+ t.expect(ds.create({name: 'foo'}), function (result) {
+ ok(false, 'ha ha ha')
+ });
+ });
+});
diff --git a/addons/web/test_support/__init__.py b/addons/web/test_support/__init__.py
new file mode 100644
index 00000000000..59f6cc67ca2
--- /dev/null
+++ b/addons/web/test_support/__init__.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+import xmlrpclib
+from ..common.openerplib.main import Connector
+
+execute_map = {}
+
+class TestConnector(Connector):
+ def db_list_lang(self):
+ return [('en_US', u'Test Language')]
+
+ def common_authenticate(self, db, login, password, environment):
+ return 87539319
+
+ def common_login(self, db, login, password):
+ return self.common_authenticate(db, login, password, {})
+
+ def object_execute_kw(self, db, uid, password, model, method, args, kwargs):
+ if model in execute_map and hasattr(execute_map[model], method):
+ return getattr(execute_map[model], method)(*args, **kwargs)
+
+ raise xmlrpclib.Fault({
+ 'model': model,
+ 'method': method,
+ 'args': args,
+ 'kwargs': kwargs
+ }, '')
+
+ def send(self, service_name, method, *args):
+ method_name = '%s_%s' % (service_name, method)
+ if hasattr(self, method_name):
+ return getattr(self, method_name)(*args)
+
+ raise xmlrpclib.Fault({
+ 'service': service_name,
+ 'method': method,
+ 'args': args
+ }, '')
diff --git a/addons/web/test_support/controllers.py b/addons/web/test_support/controllers.py
new file mode 100644
index 00000000000..f8e3c7a0739
--- /dev/null
+++ b/addons/web/test_support/controllers.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+from ..common.http import Controller, jsonrequest
+from ..controllers.main import Session
+
+UID = 87539319
+DB = 'test_db'
+LOGIN = 'test_login'
+PASSWORD = 'test_password'
+CONTEXT = {'lang': 'en_US', 'tz': 'UTC', 'uid': UID}
+
+def bind(session):
+ session.bind(DB, UID, LOGIN, PASSWORD)
+ session.context = CONTEXT
+ session.build_connection().set_login_info(DB, LOGIN, PASSWORD, UID)
+
+class TestSession(Session):
+ _cp_path = '/web/session'
+
+ def session_info(self, req):
+ if not req.session._uid:
+ bind(req.session)
+
+ return {
+ "session_id": req.session_id,
+ "uid": req.session._uid,
+ "context": CONTEXT,
+ "db": req.session._db,
+ "login": req.session._login,
+ "openerp_entreprise": False,
+ }
diff --git a/openerp-web b/openerp-web
index ec72085db2c..3340b95646e 100755
--- a/openerp-web
+++ b/openerp-web
@@ -50,6 +50,19 @@ logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(
help="Logging configuration file", metavar="FILE")
optparser.add_option_group(logging_opts)
+testing_opts = optparse.OptionGroup(optparser, "Testing")
+testing_opts.add_option('--test-mode', dest='test_mode',
+ action='store_true', default=False,
+ help="Starts test mode, which provides a few"
+ " (utterly unsafe) APIs for testing purposes and"
+ " sets up a special connector which always raises"
+ " errors on tentative server access. These errors"
+ " serialize RPC query information (service,"
+ " method, arguments list) in the fault_code"
+ " attribute of the error object returned to the"
+ " client. This lets javascript code assert the" \
+ " XMLRPC consequences of its queries.")
+optparser.add_option_group(testing_opts)
if __name__ == "__main__":
(options, args) = optparser.parse_args(sys.argv[1:])
@@ -78,6 +91,12 @@ if __name__ == "__main__":
options.backend = 'xmlrpc'
os.environ["TZ"] = "UTC"
+ if options.test_mode:
+ import web.test_support
+ import web.test_support.controllers
+ options.connector = web.test_support.TestConnector()
+ logging.getLogger('werkzeug').setLevel(logging.WARNING)
+
if sys.version_info >= (2, 7) and os.path.exists(options.log_config):
with open(options.log_config) as file:
dct = json.load(file)