[ADD] first 90% of non-sucky testing API, last 90% left
also all existing tests shoud now be completely broken and need to be fixed bzr revid: xmo@openerp.com-20121025154745-rw2gktfd6tp68k2m
This commit is contained in:
parent
1cf9b2a60d
commit
99a2dd3938
31
.bzrignore
31
.bzrignore
|
@ -1,19 +1,14 @@
|
|||
.*.swp
|
||||
.bzrignore
|
||||
.idea
|
||||
.project
|
||||
.pydevproject
|
||||
.ropeproject
|
||||
.settings
|
||||
.DS_Store
|
||||
openerp/addons/*
|
||||
openerp/filestore*
|
||||
.Python
|
||||
*.pyc
|
||||
*.pyo
|
||||
bin/*
|
||||
.*
|
||||
*.egg-info
|
||||
*.orig
|
||||
*.vim
|
||||
build/
|
||||
include/
|
||||
lib/
|
||||
share/
|
||||
doc/_build/*
|
||||
RE:^bin/
|
||||
RE:^dist/
|
||||
RE:^include/
|
||||
|
||||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
|
||||
RE:^addons/\w+/doc/_build/
|
||||
|
|
|
@ -40,6 +40,7 @@ This module provides the core of the OpenERP Web Client.
|
|||
"static/lib/cleditor/jquery.cleditor.js",
|
||||
"static/lib/py.js/lib/py.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/testing.js",
|
||||
"static/src/js/corelib.js",
|
||||
"static/src/js/coresetup.js",
|
||||
"static/src/js/dates.js",
|
||||
|
@ -67,4 +68,16 @@ This module provides the core of the OpenERP Web Client.
|
|||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
],
|
||||
'test': [
|
||||
"static/test/class.js",
|
||||
"static/test/registry.js",
|
||||
"static/test/form.js",
|
||||
"static/test/list-utils.js",
|
||||
"static/test/formats.js",
|
||||
"static/test/rpc.js",
|
||||
"static/test/evals.js",
|
||||
"static/test/search.js",
|
||||
"static/test/Widget.js",
|
||||
"static/test/list-editable.js"
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
from . import main
|
||||
from . import testing
|
||||
|
|
|
@ -800,8 +800,7 @@ class Database(openerpweb.Controller):
|
|||
|
||||
@openerpweb.jsonrequest
|
||||
def get_list(self, req):
|
||||
dbs = db_list(req)
|
||||
return {"db_list": dbs}
|
||||
return db_list(req)
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def create(self, req, fields):
|
||||
|
@ -922,10 +921,7 @@ class Session(openerpweb.Controller):
|
|||
@openerpweb.jsonrequest
|
||||
def get_lang_list(self, req):
|
||||
try:
|
||||
return {
|
||||
'lang_list': (req.session.proxy("db").list_lang() or []),
|
||||
'error': ""
|
||||
}
|
||||
return req.session.proxy("db").list_lang() or []
|
||||
except Exception, e:
|
||||
return {"error": e, "title": "Languages"}
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
# coding=utf-8
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import glob
|
||||
import itertools
|
||||
import json
|
||||
import operator
|
||||
import os
|
||||
|
||||
from mako.template import Template
|
||||
from openerp.modules import module
|
||||
|
||||
from .main import module_topological_sort
|
||||
from ..http import Controller, httprequest
|
||||
|
||||
NOMODULE_TEMPLATE = Template(u"""<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>OpenERP Testing</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/web/tests" method="GET">
|
||||
<button name="mod" value="*">Run all tests</button>
|
||||
<ul>
|
||||
% for name, module in modules:
|
||||
<li>${name} <button name="mod" value="${module}">
|
||||
Run Tests</button></li>
|
||||
% endfor
|
||||
</ul>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
NOTFOUND = Template(u"""
|
||||
<p>Unable to find the module [${module}], please check that the module
|
||||
name is correct and the module is on OpenERP's path.</p>
|
||||
<a href="/web/tests"><< Back to tests</a>
|
||||
""")
|
||||
TESTING = Template(u"""<!DOCTYPE html>
|
||||
<html style="height: 100%">
|
||||
<%def name="to_path(module, p)">/${module}/${p}</%def>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>OpenERP Web Tests</title>
|
||||
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/qunit/qunit.css">
|
||||
<script src="/web/static/lib/qunit/qunit.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
// List of modules, each module is preceded by its dependencies
|
||||
var oe_all_dependencies = ${dependencies};
|
||||
QUnit.config.testTimeout = 2000;
|
||||
</script>
|
||||
</head>
|
||||
<body id="oe" class="openerp">
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</body>
|
||||
% for module, jss, tests, templates in files:
|
||||
% for js in jss:
|
||||
<script src="${to_path(module, js)}"></script>
|
||||
% endfor
|
||||
% if tests or templates:
|
||||
<script>
|
||||
openerp.testing.current_module = "${module}";
|
||||
% for template in templates:
|
||||
openerp.testing.add_template("${to_path(module, template)}");
|
||||
% endfor
|
||||
</script>
|
||||
% endif
|
||||
% if tests:
|
||||
% for test in tests:
|
||||
<script type="text/javascript" src="${to_path(module, test)}"></script>
|
||||
% endfor
|
||||
% endif
|
||||
% endfor
|
||||
</html>
|
||||
""")
|
||||
|
||||
class TestRunnerController(Controller):
|
||||
_cp_path = '/web/tests'
|
||||
|
||||
@httprequest
|
||||
def index(self, req, mod=None, **kwargs):
|
||||
ms = module.get_modules()
|
||||
manifests = dict(
|
||||
(name, desc)
|
||||
for name, desc in zip(ms, map(self.load_manifest, ms))
|
||||
if desc # remove not-actually-openerp-modules
|
||||
)
|
||||
|
||||
if not mod:
|
||||
return NOMODULE_TEMPLATE.render(modules=(
|
||||
(manifest['name'], name)
|
||||
for name, manifest in manifests.iteritems()
|
||||
if any(testfile.endswith('.js')
|
||||
for testfile in manifest['test'])
|
||||
))
|
||||
sorted_mods = module_topological_sort(dict(
|
||||
(name, manifest.get('depends', []))
|
||||
for name, manifest in manifests.iteritems()
|
||||
))
|
||||
# to_load and to_test should be zippable lists of the same length.
|
||||
# A falsy value in to_test indicate nothing to test at that index (just
|
||||
# load the corresponding part of to_load)
|
||||
to_test = sorted_mods
|
||||
if mod != '*':
|
||||
if mod not in manifests:
|
||||
return req.not_found(NOTFOUND.render(module=mod))
|
||||
idx = sorted_mods.index(mod)
|
||||
to_test = [None] * len(sorted_mods)
|
||||
to_test[idx] = mod
|
||||
|
||||
tests_candicates = [
|
||||
filter(lambda path: path.endswith('.js'),
|
||||
manifests[mod]['test'] if mod else [])
|
||||
for mod in to_test]
|
||||
# remove trailing test-less modules
|
||||
tests = reversed(list(
|
||||
itertools.dropwhile(
|
||||
operator.not_,
|
||||
reversed(tests_candicates))))
|
||||
|
||||
files = [
|
||||
(mod, manifests[mod]['js'], tests, manifests[mod]['qweb'])
|
||||
for mod, tests in itertools.izip(sorted_mods, tests)
|
||||
]
|
||||
|
||||
return TESTING.render(files=files, dependencies=json.dumps(
|
||||
[name for name in sorted_mods
|
||||
if module.get_module_resource(name, 'static')
|
||||
if manifests[name]['js']]))
|
||||
|
||||
def load_manifest(self, name):
|
||||
manifest = module.load_information_from_description_file(name)
|
||||
if manifest:
|
||||
path = module.get_module_path(name)
|
||||
manifest['js'] = list(
|
||||
self.expand_patterns(path, manifest.get('js', [])))
|
||||
manifest['test'] = list(
|
||||
self.expand_patterns(path, manifest.get('test', [])))
|
||||
manifest['qweb'] = list(
|
||||
self.expand_patterns(path, manifest.get('qweb', [])))
|
||||
return manifest
|
||||
|
||||
def expand_patterns(self, root, patterns):
|
||||
for pattern in patterns:
|
||||
normalized_pattern = os.path.normpath(os.path.join(root, pattern))
|
||||
for path in glob.glob(normalized_pattern):
|
||||
# replace OS path separators (from join & normpath) by URI ones
|
||||
yield path[len(root):].replace(os.path.sep, '/')
|
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
|
@ -24,6 +24,8 @@ Contents:
|
|||
|
||||
guides/client-action
|
||||
|
||||
testing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
|
|
|
@ -0,0 +1,479 @@
|
|||
.. highlight:: javascript
|
||||
|
||||
Testing in OpenERP Web
|
||||
======================
|
||||
|
||||
Javascript Unit Testing
|
||||
-----------------------
|
||||
|
||||
OpenERP Web 7.0 includes means to unit-test both the core code of
|
||||
OpenERP Web and your own javascript modules. On the javascript side,
|
||||
unit-testing is based on QUnit_ with a number of helpers and
|
||||
extensions for better integration with OpenERP.
|
||||
|
||||
To see what the runner looks like, find (or start) an OpenERP server
|
||||
with the web client enabled, and navigate to ``/web/tests`` e.g. `on
|
||||
OpenERP's CI <http://trunk.runbot.openerp.com/web/tests>`_. This will
|
||||
show the runner selector, which lists all modules with javascript unit
|
||||
tests, and allows starting any of them (or all javascript tests in all
|
||||
modules at once).
|
||||
|
||||
.. image:: ./images/runner.png
|
||||
:align: center
|
||||
|
||||
Clicking any runner button will launch the corresponding tests in the
|
||||
bundled QUnit_ runner:
|
||||
|
||||
.. image:: ./images/tests.png
|
||||
:align: center
|
||||
|
||||
Writing a test case
|
||||
-------------------
|
||||
|
||||
The first step is to list the test file(s). This is done through the
|
||||
``test`` key of the openerp manifest, by adding javascript files to it
|
||||
(next to the usual YAML files, if any):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
'name': "Demonstration of web/javascript tests",
|
||||
'category': 'Hidden',
|
||||
'depends': ['web'],
|
||||
'test': ['static/test/demo.js'],
|
||||
}
|
||||
|
||||
and to create the corresponding test file(s)
|
||||
|
||||
.. note::
|
||||
|
||||
test files which do not exist will be ignored, if all test files
|
||||
of a module are ignored (can not be found), the test runner will
|
||||
consider that the module has no javascript tests
|
||||
|
||||
After that, refreshing the runner selector will display the new module
|
||||
and allow running all of its (0 so far) tests:
|
||||
|
||||
.. image:: ./images/runner2.png
|
||||
:align: center
|
||||
|
||||
The next step is to create a test case::
|
||||
|
||||
openerp.testing.section('basic section', function (test) {
|
||||
test('my first test', function () {
|
||||
ok(false, "this test has run");
|
||||
});
|
||||
});
|
||||
|
||||
All testing helpers and structures live in the ``openerp.testing``
|
||||
module. OpenERP tests live in a :js:func:`~openerp.testing.section`,
|
||||
which is itself part of a module. The first argument to a section is
|
||||
the name of the section, the second one is the section body.
|
||||
|
||||
:js:func:`~openerp.testing.test`, provided by the
|
||||
:js:func:`~openerp.testing.section` to the callback, is used to
|
||||
register a given test case which will be run whenever the test runner
|
||||
actually does its job. OpenERP Web test case use standard `QUnit
|
||||
assertions`_ within them.
|
||||
|
||||
Launching the test runner at this point will run the test and display
|
||||
the corresponding assertion message, with red colors indicating the
|
||||
test failed:
|
||||
|
||||
.. image:: ./images/tests2.png
|
||||
:align: center
|
||||
|
||||
Fixing the test (by replacing ``false`` to ``true`` in the assertion)
|
||||
will make it pass:
|
||||
|
||||
.. image:: ./images/tests3.png
|
||||
:align: center
|
||||
|
||||
Assertions
|
||||
----------
|
||||
|
||||
As noted above, OpenERP Web's tests use `qunit assertions`_. They are
|
||||
available globally (so they can just be called without references to
|
||||
anything). The following list is available:
|
||||
|
||||
.. js:function:: ok(state[, message])
|
||||
|
||||
checks that ``state`` is truthy (in the javascript sense)
|
||||
|
||||
.. js:function:: strictEqual(actual, expected[, message])
|
||||
|
||||
checks that the actual (produced by a method being tested) and
|
||||
expected values are identical (roughly equivalent to ``ok(actual
|
||||
=== expected, message)``)
|
||||
|
||||
.. js:function:: notStrictEqual(actual, expected[, message])
|
||||
|
||||
checks that the actual and expected values are *not* identical
|
||||
(roughly equivalent to ``ok(actual !== expected, message)``)
|
||||
|
||||
.. js:function:: deepEqual(actual, expected[, message])
|
||||
|
||||
deep comparison between actual and expected: recurse into
|
||||
containers (objects and arrays) to ensure that they have the same
|
||||
keys/number of elements, and the values match.
|
||||
|
||||
.. js:function:: notDeepEqual(actual, expected[, message])
|
||||
|
||||
inverse operation to :js:func:`deepEqual`
|
||||
|
||||
.. js:function:: throws(block[, expected][, message])
|
||||
|
||||
checks that, when called, the ``block`` throws an
|
||||
error. Optionally validates that error against ``expected``.
|
||||
|
||||
:param Function block:
|
||||
:param expected: if a regexp, checks that the thrown error's
|
||||
message matches the regular expression. If an
|
||||
error type, checks that the thrown error is of
|
||||
that type.
|
||||
:type expected: Error | RegExp
|
||||
|
||||
.. js:function:: equal(actual, expected[, message])
|
||||
|
||||
checks that ``actual`` and ``expected`` are loosely equal, using
|
||||
the ``==`` operator and its coercion rules.
|
||||
|
||||
.. js:function:: notEqual(actual, expected[, message])
|
||||
|
||||
inverse operation to :js:func:`equal`
|
||||
|
||||
Getting an OpenERP instance
|
||||
---------------------------
|
||||
|
||||
The OpenERP instance is the base through which most OpenERP Web
|
||||
modules behaviors (functions, objects, …) are accessed. As a result,
|
||||
the test framework automatically builds one, and loads the module
|
||||
being tested and all of its dependencies inside it. This new instance
|
||||
is provided as the first positional parameter to your test
|
||||
cases. Let's observe by adding javascript code (not test code) to the
|
||||
test module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
'name': "Demonstration of web/javascript tests",
|
||||
'category': 'Hidden',
|
||||
'depends': ['web'],
|
||||
'js': ['static/src/js/demo.js'],
|
||||
'test': ['static/test/demo.js'],
|
||||
}
|
||||
|
||||
::
|
||||
|
||||
// src/js/demo.js
|
||||
openerp.web_tests_demo = function (instance) {
|
||||
instance.web_tests_demo = {
|
||||
value_true: true,
|
||||
SomeType: instance.web.Class.extend({
|
||||
init: function (value) {
|
||||
this.value = value;
|
||||
}
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
and then adding a new test case, which simply checks that the
|
||||
``instance`` contains all the expected stuff we created in the
|
||||
module::
|
||||
|
||||
// test/demo.js
|
||||
test('module content', function (instance) {
|
||||
ok(instance.web_tests_demo.value_true, "should have a true value");
|
||||
var type_instance = new instance.web_tests_demo.SomeType(42);
|
||||
strictEqual(type_instance.value, 42, "should have provided value");
|
||||
});
|
||||
|
||||
DOM Scratchpad
|
||||
--------------
|
||||
|
||||
As in the wider client, arbitrarily accessing document content is
|
||||
strongly discouraged during tests. But DOM access is still needed to
|
||||
e.g. fully initialize :js:class:`widgets <~openerp.web.Widget>` before
|
||||
testing them.
|
||||
|
||||
Thus, test cases get a DOM scratchpad as its second positional
|
||||
parameter, in a jQuery instance. That scratchpad is fully cleaned up
|
||||
before each test, and as long as it doesn't do anything outside the
|
||||
scrartchpad your code can do whatever it wants::
|
||||
|
||||
// test/demo.js
|
||||
test('DOM content', function (instance, $scratchpad) {
|
||||
$scratchpad.html('<div><span class="foo bar">ok</span></div>');
|
||||
ok($scratchpad.find('span').hasClass('foo'),
|
||||
"should have provided class");
|
||||
});
|
||||
test('clean scratchpad', function (instance, $scratchpad) {
|
||||
ok(!$scratchpad.children().length, "should have no content");
|
||||
ok(!$scratchpad.text(), "should have no text");
|
||||
});
|
||||
|
||||
.. note::
|
||||
|
||||
the top-level element of the scratchpad is not cleaned up, test
|
||||
cases can add text or DOM children but shoud not alter
|
||||
``$scratchpad`` itself.
|
||||
|
||||
Loading templates
|
||||
-----------------
|
||||
|
||||
To avoid the corresponding processing costs, by default templates are
|
||||
not loaded into QWeb. If you need to render e.g. widgets making use of
|
||||
QWeb templates, you can request their loading through the
|
||||
:js:attr:`~TestOptions.templates` option.
|
||||
|
||||
This will automatically load all relevant templates in the instance's
|
||||
qweb before running the test case:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
'name': "Demonstration of web/javascript tests",
|
||||
'category': 'Hidden',
|
||||
'depends': ['web'],
|
||||
'js': ['static/src/js/demo.js'],
|
||||
'test': ['static/test/demo.js'],
|
||||
'qweb': ['static/src/xml/demo.xml'],
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- src/xml/demo.xml -->
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="DemoTemplate">
|
||||
<t t-foreach="5" t-as="value">
|
||||
<p><t t-esc="value"/></p>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
::
|
||||
|
||||
// test/demo.js
|
||||
test('templates', {templates: true}, function (instance) {
|
||||
var s = instance.web.qweb.render('DemoTemplate');
|
||||
var texts = $(s).find('p').map(function () {
|
||||
return $(this).text();
|
||||
}).get();
|
||||
|
||||
deepEqual(texts, ['0', '1', '2', '3', '4']);
|
||||
});
|
||||
|
||||
Asynchronous cases
|
||||
------------------
|
||||
|
||||
The test case examples so far are all synchronous, they execute from
|
||||
the first to the last line and once the last line has executed the
|
||||
test is done. But the web client is full of :doc:`asynchronous code
|
||||
</async>`, and thus test cases need to be async-aware.
|
||||
|
||||
This is done by returning a :js:class:`deferred <Deferred>` from the
|
||||
case callback::
|
||||
|
||||
// test/demo.js
|
||||
test('asynchronous', {
|
||||
asserts: 1
|
||||
}, function () {
|
||||
var d = $.Deferred();
|
||||
setTimeout(function () {
|
||||
ok(true);
|
||||
d.resolve();
|
||||
}, 100);
|
||||
return d;
|
||||
});
|
||||
|
||||
This example also introduces an options object to the test case. In
|
||||
this case, it's used to specify the number of assertions the test case
|
||||
should expect, if less or more assertions are specified the case will
|
||||
count as failed.
|
||||
|
||||
Asynchronous test cases *must* specify the number of assertions they
|
||||
will run. This allows more easily catching situations where e.g. the
|
||||
test architecture was not warned about asynchronous operations.
|
||||
|
||||
.. note::
|
||||
|
||||
asynchronous test cases also have a 2 seconds timeout: if the test
|
||||
does not finish within 2 seconds, it will be considered
|
||||
failed. This pretty much always means the test will not resolve.
|
||||
|
||||
.. note::
|
||||
|
||||
if the returned deferred is rejected, the test will be failed
|
||||
unless :js:attr:`~TestOptions.fail_on_rejection` is set to
|
||||
``false``.
|
||||
|
||||
RPC
|
||||
---
|
||||
|
||||
An important subset of asynchronous test cases is test cases which
|
||||
need to perform (and chain, to an extent) RPC calls.
|
||||
|
||||
.. note::
|
||||
|
||||
because they are a subset of asynchronous cases, RPC cases must
|
||||
also provide a valid :js:attr:`assertions count
|
||||
<TestOptions.asserts>`
|
||||
|
||||
By default, test cases will fail when trying to perform an RPC
|
||||
call. The ability to perform RPC calls must be explicitly requested by
|
||||
a test case (or its containing test suite) through
|
||||
:js:attr:`~TestOptions.rpc`, and can be one of two modes: ``mock`` or
|
||||
``rpc``.
|
||||
|
||||
Mock RPC
|
||||
++++++++
|
||||
|
||||
The preferred (and most fastest from a setup and execution time point
|
||||
of view) way to do RPC during tests is to mock the RPC calls: while
|
||||
setting up the test case, provide what the RPC responses "should" be,
|
||||
and only test the code between the "user" (the test itself) and the
|
||||
RPC call, before the call is effectively done.
|
||||
|
||||
To do this, set the :js:attr:`rpc option <~TestOptions.rpc>` to
|
||||
``mock``. This will add a third parameter to the test case callback:
|
||||
|
||||
.. js:function:: mock(rpc_spec, handler)
|
||||
|
||||
Can be used in two different ways depending on the shape of the
|
||||
first parameter:
|
||||
|
||||
* If it matches the pattern ``model:method`` (if it contains a
|
||||
colon, essentially) the call will set up the mocking of an RPC
|
||||
call straight to the OpenERP server (through XMLRPC) as
|
||||
performed via e.g. :js:func:`openerp.web.Model.call`.
|
||||
|
||||
In that case, ``handler`` should be a function taking two
|
||||
arguments ``args`` and ``kwargs``, matching the corresponding
|
||||
arguments on the server side. Hander should simply return the
|
||||
value as if it were returned by the Python XMLRPC handler::
|
||||
|
||||
test('XML-RPC', {rpc: 'mock', asserts: 3}, function (instance, $s, mock) {
|
||||
// set up mocking
|
||||
mock('people.famous:name_search', function (args, kwargs) {
|
||||
strictEqual(kwargs.name, 'bob');
|
||||
return [
|
||||
[1, "Microsoft Bob"],
|
||||
[2, "Bob the Builder"],
|
||||
[3, "Silent Bob"]
|
||||
];
|
||||
});
|
||||
|
||||
// actual test code
|
||||
return new instance.web.Model('people.famous')
|
||||
.call('name_search', {name: 'bob'}).pipe(function (result) {
|
||||
strictEqual(result.length, 3, "shoud return 3 people");
|
||||
strictEqual(result[0][1], "Microsoft Bob",
|
||||
"the most famous bob should be Microsoft Bob");
|
||||
});
|
||||
});
|
||||
|
||||
* Otherwise, if it matches an absolute path (e.g. ``/a/b/c``) it
|
||||
will mock a JSON-RPC call to a web client controller, such as
|
||||
``/web/webclient/translations``. In that case, the handler takes
|
||||
a single ``params`` argument holding all of the parameters
|
||||
provided over JSON-RPC.
|
||||
|
||||
As previously, the handler should simply return the result value
|
||||
as if returned by the original JSON-RPC handler::
|
||||
|
||||
test('JSON-RPC', {rpc: 'mock', asserts: 3, templates: true}, function (instance, $s, mock) {
|
||||
var fetched_dbs = false, fetched_langs = false;
|
||||
mock('/web/database/get_list', function () {
|
||||
fetched_dbs = true;
|
||||
return ['foo', 'bar', 'baz'];
|
||||
});
|
||||
mock('/web/session/get_lang_list', function () {
|
||||
fetched_langs = true;
|
||||
return [['vo_IS', 'Hopelandic / Vonlenska']];
|
||||
});
|
||||
|
||||
// widget needs that or it blows up
|
||||
instance.webclient = {toggle_bars: openerp.testing.noop};
|
||||
var dbm = new instance.web.DatabaseManager({});
|
||||
return dbm.appendTo($s).pipe(function () {
|
||||
ok(fetched_dbs, "should have fetched databases");
|
||||
ok(fetched_langs, "should have fetched languages");
|
||||
deepEqual(dbm.db_list, ['foo', 'bar', 'baz']);
|
||||
});
|
||||
});
|
||||
|
||||
.. note::
|
||||
|
||||
mock handlers can contain assertions, these assertions should be
|
||||
part of the assertions count (and if multiple calls are made to a
|
||||
handler containing assertions, it multiplies the effective number
|
||||
of assertions)
|
||||
|
||||
Actual RPC
|
||||
++++++++++
|
||||
|
||||
.. TODO:: rpc to database (implement & document)
|
||||
|
||||
Testing API
|
||||
-----------
|
||||
|
||||
.. todo:: implement options on sections
|
||||
|
||||
.. js:class:: TestOptions
|
||||
|
||||
the various options which can be passed to
|
||||
:js:func:`~openerp.testing.section` or
|
||||
:js:func:`~openerp.testing.case`
|
||||
|
||||
.. js:attribute:: TestOptions.asserts
|
||||
|
||||
An integer, the number of assertions which should run during a
|
||||
normal execution of the test. Mandatory for asynchronous tests.
|
||||
|
||||
.. js:attribute:: TestOptions.setup
|
||||
|
||||
.. todo:: implement & document setup (async?)
|
||||
|
||||
.. js:attribute:: TestOptions.teardown
|
||||
|
||||
.. todo:: implement & document teardown (async?)
|
||||
|
||||
.. js:attribute:: TestOptions.fail_on_rejection
|
||||
|
||||
If the test is asynchronous and its resulting promise is
|
||||
rejected, fail the test. Defaults to ``true``, set to
|
||||
``false`` to not fail the test in case of rejection::
|
||||
|
||||
// test/demo.js
|
||||
test('unfail rejection', {
|
||||
asserts: 1,
|
||||
fail_on_rejection: false
|
||||
}, function () {
|
||||
var d = $.Deferred();
|
||||
setTimeout(function () {
|
||||
ok(true);
|
||||
d.reject();
|
||||
}, 100);
|
||||
return d;
|
||||
});
|
||||
|
||||
.. js:attribute:: TestOptions.rpc
|
||||
|
||||
RPC method to use during tests, one of ``"mock"`` or
|
||||
``"rpc"``. Any other value will disable RPC for the test (if
|
||||
they were enabled by the suite for instance).
|
||||
|
||||
.. js:attribute:: TestOptions.templates
|
||||
|
||||
Whether the current module (and its dependencies)'s templates
|
||||
should be loaded into QWeb before starting the test. A
|
||||
boolean, ``false`` by default.
|
||||
|
||||
Running through Python
|
||||
----------------------
|
||||
|
||||
.. todo:: make that work and document it
|
||||
|
||||
.. _qunit: http://qunitjs.com/
|
||||
|
||||
.. _qunit assertions: http://api.qunitjs.com/category/assert/
|
|
@ -537,7 +537,7 @@ class Root(object):
|
|||
:rtype: ``Controller | None``
|
||||
"""
|
||||
if l:
|
||||
ps = '/' + '/'.join(l)
|
||||
ps = '/' + '/'.join(filter(None, l))
|
||||
meth = 'index'
|
||||
while ps:
|
||||
c = controllers_path.get(ps)
|
||||
|
|
|
@ -296,14 +296,14 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
|||
$('.oe_secondary_menus_container,.oe_user_menu_placeholder').empty();
|
||||
var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
|
||||
function(result) {
|
||||
self.db_list = result.db_list;
|
||||
self.db_list = result;
|
||||
},
|
||||
function (_, ev) {
|
||||
ev.preventDefault();
|
||||
self.db_list = null;
|
||||
});
|
||||
var fetch_langs = this.rpc("/web/session/get_lang_list", {}).then(function(result) {
|
||||
self.lang_list = result.lang_list;
|
||||
self.lang_list = result;
|
||||
});
|
||||
return $.when(fetch_db, fetch_langs).then(self.do_render);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
// Test support structures and methods for OpenERP
|
||||
openerp.testing = {};
|
||||
(function (testing) {
|
||||
var dependencies = {
|
||||
corelib: [],
|
||||
coresetup: ['corelib'],
|
||||
data: ['corelib', 'coresetup'],
|
||||
dates: [],
|
||||
formats: ['coresetup', 'dates'],
|
||||
chrome: ['corelib', 'coresetup'],
|
||||
views: ['corelib', 'coresetup', 'data', 'chrome'],
|
||||
search: ['data', 'coresetup', 'formats'],
|
||||
list: ['views', 'data'],
|
||||
form: ['data', 'views', 'list', 'formats'],
|
||||
list_editable: ['list', 'form', 'data'],
|
||||
};
|
||||
|
||||
testing.dependencies = window['oe_all_dependencies'] || [];
|
||||
testing.current_module = null;
|
||||
testing.templates = { };
|
||||
testing.add_template = function (name) {
|
||||
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||
xhr.open('GET', name, false);
|
||||
xhr.send(null);
|
||||
(testing.templates[testing.current_module] =
|
||||
testing.templates[testing.current_module] || [])
|
||||
.push(xhr.responseXML);
|
||||
};
|
||||
/**
|
||||
* Function which does not do anything
|
||||
*/
|
||||
testing.noop = function () { };
|
||||
/**
|
||||
* Alter provided instance's ``session`` attribute to make response
|
||||
* mockable:
|
||||
*
|
||||
* * The ``responses`` parameter can be used to provide a map of (RPC)
|
||||
* paths (e.g. ``/web/view/load``) to a function returning a response
|
||||
* to the query.
|
||||
* * ``instance.session`` grows a ``responses`` attribute which is
|
||||
* a map of the same (and is in fact initialized to the ``responses``
|
||||
* parameter if one is provided)
|
||||
*
|
||||
* Note that RPC requests to un-mocked URLs will be rejected with an
|
||||
* error message: only explicitly specified urls will get a response.
|
||||
*
|
||||
* Mocked sessions will *never* perform an actual RPC connection.
|
||||
*
|
||||
* @param instance openerp instance being initialized
|
||||
* @param {Object} [responses]
|
||||
*/
|
||||
testing.mockifyRPC = function (instance, responses) {
|
||||
var session = instance.session;
|
||||
session.responses = responses || {};
|
||||
session.rpc_function = function (url, payload) {
|
||||
var fn, params;
|
||||
var needle = payload.params.model + ':' + payload.params.method;
|
||||
if (url.url === '/web/dataset/call_kw'
|
||||
&& needle in this.responses) {
|
||||
fn = this.responses[needle];
|
||||
params = [
|
||||
payload.params.args || [],
|
||||
payload.params.kwargs || {}
|
||||
];
|
||||
} else {
|
||||
fn = this.responses[url.url];
|
||||
params = [payload];
|
||||
}
|
||||
|
||||
if (!fn) {
|
||||
return $.Deferred().reject({}, 'failed',
|
||||
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
|
||||
url.url, JSON.stringify(payload.params))
|
||||
).promise();
|
||||
}
|
||||
try {
|
||||
return $.when(fn.apply(null, params)).pipe(function (result) {
|
||||
// Wrap for RPC layer unwrapper thingy
|
||||
return {result: result};
|
||||
});
|
||||
} catch (e) {
|
||||
// not sure why this looks like that
|
||||
return $.Deferred().reject({}, 'failed', String(e));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var _load = function (instance, module, loaded) {
|
||||
if (!loaded) { loaded = []; }
|
||||
|
||||
var deps = dependencies[module];
|
||||
if (!deps) { throw new Error("Unknown dependencies for " + module); }
|
||||
|
||||
var to_load = _.difference(deps, loaded);
|
||||
while (!_.isEmpty(to_load)) {
|
||||
_load(instance, to_load[0], loaded);
|
||||
to_load = _.difference(deps, loaded);
|
||||
}
|
||||
openerp.web[module](instance);
|
||||
loaded.push(module);
|
||||
};
|
||||
|
||||
testing.section = function (name, body) {
|
||||
QUnit.module(testing.current_module + '.' + name);
|
||||
body(testing.case);
|
||||
};
|
||||
testing.case = function (name, options, callback) {
|
||||
if (_.isFunction(options)) {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
var module = testing.current_module;
|
||||
var module_index = _.indexOf(testing.dependencies, module);
|
||||
var module_deps = testing.dependencies.slice(
|
||||
// If module not in deps (because only tests, no JS) -> indexOf
|
||||
// returns -1 -> index becomes 0 -> replace with ``undefined`` so
|
||||
// Array#slice returns a full copy
|
||||
0, module_index + 1 || undefined);
|
||||
QUnit.test(name, function (env) {
|
||||
var instance = openerp.init(module_deps);
|
||||
if (_.isNumber(options.asserts)) {
|
||||
expect(options.asserts)
|
||||
}
|
||||
|
||||
if (options.templates) {
|
||||
for(var i=0; i<module_deps.length; ++i) {
|
||||
var dep = module_deps[i];
|
||||
var templates = testing.templates[dep];
|
||||
if (_.isEmpty(templates)) { continue; }
|
||||
|
||||
for (var j=0; j < templates.length; ++j) {
|
||||
instance.web.qweb.add_template(templates[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mock, async = false;
|
||||
switch (options.rpc) {
|
||||
case 'mock':
|
||||
async = true;
|
||||
testing.mockifyRPC(instance);
|
||||
mock = function (spec, handler) {
|
||||
instance.session.responses[spec] = handler;
|
||||
};
|
||||
break;
|
||||
case 'rpc':
|
||||
async = true;
|
||||
}
|
||||
|
||||
// TODO: explicit dependencies options for web sub-modules (will deprecate _load/instanceFor)
|
||||
var result = callback(instance, $('#qunit-fixture'), mock);
|
||||
|
||||
if (!(result && _.isFunction(result.then))) {
|
||||
if (async) {
|
||||
ok(false, "asynchronous test cases must return a promise");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stop();
|
||||
if (!_.isNumber(options.asserts)) {
|
||||
ok(false, "asynchronous test cases must specify the "
|
||||
+ "number of assertions they expect");
|
||||
}
|
||||
result.then(function () {
|
||||
start();
|
||||
}, function (error) {
|
||||
start();
|
||||
if (options.fail_on_rejection === false) {
|
||||
return;
|
||||
}
|
||||
ok(false, typeof error === 'object' && error.message
|
||||
? error.message
|
||||
: JSON.stringify([].slice.call(arguments)));
|
||||
})
|
||||
});
|
||||
};
|
||||
})(openerp.testing);
|
|
@ -776,8 +776,8 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
|
|||
name: "JS Tests",
|
||||
target: 'new',
|
||||
type : 'ir.actions.act_url',
|
||||
url: '/web/static/test/test.html'
|
||||
})
|
||||
url: '/web/tests?mod=*'
|
||||
});
|
||||
break;
|
||||
case 'perm_read':
|
||||
var ids = current_view.get_selected_ids();
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html style="height: 100%">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>OpenERP Web Test Suite</title>
|
||||
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/qunit/qunit.css">
|
||||
<script src="/web/static/lib/qunit/qunit.js" type="text/javascript"></script>
|
||||
|
||||
<script src="/web/static/lib/underscore/underscore.js" type="text/javascript"></script>
|
||||
<script src="/web/static/lib/underscore/underscore.string.js" type="text/javascript"></script>
|
||||
<script src="/web/static/lib/backbone/backbone.js" type="text/javascript"></script>
|
||||
|
||||
<!-- jquery -->
|
||||
<script src="/web/static/lib/jquery/jquery-1.7.2.js"></script>
|
||||
<script src="/web/static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js"></script>
|
||||
<script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
|
||||
<script src="/web/static/lib/datejs/globalization/en-US.js"></script>
|
||||
<script src="/web/static/lib/datejs/core.js"></script>
|
||||
<script src="/web/static/lib/datejs/parser.js"></script>
|
||||
<script src="/web/static/lib/datejs/sugarpak.js"></script>
|
||||
<script src="/web/static/lib/datejs/extras.js"></script>
|
||||
|
||||
<script src="/web/static/lib/qweb/qweb2.js"></script>
|
||||
|
||||
<script src="/web/static/lib/py.js/lib/py.js"></script>
|
||||
|
||||
<script src="/web/static/src/js/boot.js"></script>
|
||||
<script src="/web/static/src/js/corelib.js"></script>
|
||||
<script src="/web/static/src/js/coresetup.js"></script>
|
||||
<script src="/web/static/src/js/dates.js"></script>
|
||||
<script src="/web/static/src/js/formats.js"></script>
|
||||
<script src="/web/static/src/js/chrome.js"></script>
|
||||
<script src="/web/static/src/js/data.js"></script>
|
||||
<script src="/web/static/src/js/views.js"></script>
|
||||
<script src="/web/static/src/js/search.js"></script>
|
||||
<script src="/web/static/src/js/view_form.js"></script>
|
||||
<script src="/web/static/src/js/view_list.js"></script>
|
||||
<script src="/web/static/src/js/view_list_editable.js"></script>
|
||||
|
||||
<script src="/web/static/test/testing.js"></script>
|
||||
<script type="text/javascript">
|
||||
QUnit.config.testTimeout = 2000;
|
||||
</script>
|
||||
</head>
|
||||
<body id="oe" class="openerp">
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</body>
|
||||
<script type="text/javascript" src="/web/static/test/class.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/registry.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/form.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/list-utils.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/formats.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/rpc.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/evals.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/search.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/Widget.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/list-editable.js"></script>
|
||||
</html>
|
|
@ -1,97 +0,0 @@
|
|||
// Test support structures and methods for OpenERP
|
||||
openerp.testing = (function () {
|
||||
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||
xhr.open('GET', '/web/static/src/xml/base.xml', false);
|
||||
xhr.send(null);
|
||||
var doc = xhr.responseXML;
|
||||
|
||||
var dependencies = {
|
||||
corelib: [],
|
||||
coresetup: ['corelib'],
|
||||
data: ['corelib', 'coresetup'],
|
||||
dates: [],
|
||||
formats: ['coresetup', 'dates'],
|
||||
chrome: ['corelib', 'coresetup'],
|
||||
views: ['corelib', 'coresetup', 'data', 'chrome'],
|
||||
search: ['data', 'coresetup', 'formats'],
|
||||
list: ['views', 'data'],
|
||||
form: ['data', 'views', 'list', 'formats'],
|
||||
list_editable: ['list', 'form', 'data'],
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Function which does not do anything
|
||||
*/
|
||||
noop: function () { },
|
||||
/**
|
||||
* Loads 'base.xml' template file into qweb for the provided instance
|
||||
*
|
||||
* @param instance openerp instance being initialized, to load the template file in
|
||||
*/
|
||||
loadTemplate: function (instance) {
|
||||
instance.web.qweb.add_template(doc);
|
||||
},
|
||||
/**
|
||||
* Alter provided instance's ``session`` attribute to make response
|
||||
* mockable:
|
||||
*
|
||||
* * The ``responses`` parameter can be used to provide a map of (RPC)
|
||||
* paths (e.g. ``/web/view/load``) to a function returning a response
|
||||
* to the query.
|
||||
* * ``instance.session`` grows a ``responses`` attribute which is
|
||||
* a map of the same (and is in fact initialized to the ``responses``
|
||||
* parameter if one is provided)
|
||||
*
|
||||
* Note that RPC requests to un-mocked URLs will be rejected with an
|
||||
* error message: only explicitly specified urls will get a response.
|
||||
*
|
||||
* Mocked sessions will *never* perform an actual RPC connection.
|
||||
*
|
||||
* @param instance openerp instance being initialized
|
||||
* @param {Object} [responses]
|
||||
*/
|
||||
mockifyRPC: function (instance, responses) {
|
||||
var session = instance.session;
|
||||
session.responses = responses || {};
|
||||
session.rpc_function = function (url, payload) {
|
||||
var fn = this.responses[url.url + ':' + payload.params.method]
|
||||
|| this.responses[url.url];
|
||||
|
||||
if (!fn) {
|
||||
return $.Deferred().reject({}, 'failed',
|
||||
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
|
||||
url.url, JSON.stringify(payload.params))
|
||||
).promise();
|
||||
}
|
||||
return $.when(fn(payload));
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Creates an openerp web instance loading the specified module after
|
||||
* all of its dependencies.
|
||||
*
|
||||
* @param {String} module
|
||||
* @returns OpenERP Web instance
|
||||
*/
|
||||
instanceFor: function (module) {
|
||||
var instance = openerp.init([]);
|
||||
this._load(instance, module);
|
||||
return instance;
|
||||
},
|
||||
_load: function (instance, module, loaded) {
|
||||
if (!loaded) { loaded = []; }
|
||||
|
||||
var deps = dependencies[module];
|
||||
if (!deps) { throw new Error("Unknown dependencies for " + module); }
|
||||
|
||||
var to_load = _.difference(deps, loaded);
|
||||
while (!_.isEmpty(to_load)) {
|
||||
this._load(instance, to_load[0], loaded);
|
||||
to_load = _.difference(deps, loaded);
|
||||
}
|
||||
openerp.web[module](instance);
|
||||
loaded.push(module);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -9,9 +9,7 @@
|
|||
*/
|
||||
/*global module:true, define:true*/
|
||||
!function (name, context, definition) {
|
||||
if (typeof module !== 'undefined') module.exports = definition(name, context);
|
||||
else if (typeof define === 'function' && typeof define.amd === 'object') define(definition);
|
||||
else context[name] = definition(name, context);
|
||||
context[name] = definition(name, context);
|
||||
}('bean', this, function (name, context) {
|
||||
var win = window
|
||||
, old = context[name]
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
'name': "Demonstration of web/javascript tests",
|
||||
'category': 'Hidden',
|
||||
'depends': ['web'],
|
||||
'js': ['static/src/js/demo.js'],
|
||||
'test': ['static/test/demo.js'],
|
||||
'qweb': ['static/src/xml/demo.xml'],
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// static/src/js/demo.js
|
||||
openerp.web_tests_demo = function (instance) {
|
||||
instance.web_tests_demo = {
|
||||
value_true: true,
|
||||
SomeType: instance.web.Class.extend({
|
||||
init: function (value) {
|
||||
this.value = value;
|
||||
}
|
||||
})
|
||||
};
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
<templates id="template" xml:space="preserve">
|
||||
<div t-name="DemoTemplate">
|
||||
<t t-foreach="5" t-as="value">
|
||||
<p><t t-esc="value"/></p>
|
||||
</t>
|
||||
</div>
|
||||
</templates>
|
|
@ -0,0 +1,87 @@
|
|||
openerp.testing.section('basic section', function (test) {
|
||||
test('my first test', function () {
|
||||
ok(true, "this test has run");
|
||||
});
|
||||
test('module content', function (instance) {
|
||||
ok(instance.web_tests_demo.value_true, "should have a true value");
|
||||
var type_instance = new instance.web_tests_demo.SomeType(42);
|
||||
strictEqual(type_instance.value, 42, "should have provided value");
|
||||
});
|
||||
test('DOM content', function (instance, $scratchpad) {
|
||||
$scratchpad.html('<div><span class="foo bar">ok</span></div>');
|
||||
ok($scratchpad.find('span').hasClass('foo'),
|
||||
"should have provided class");
|
||||
});
|
||||
test('clean scratchpad', function (instance, $scratchpad) {
|
||||
ok(!$scratchpad.children().length, "should have no content");
|
||||
ok(!$scratchpad.text(), "should have no text");
|
||||
});
|
||||
|
||||
test('templates', {templates: true}, function (instance) {
|
||||
var s = instance.web.qweb.render('DemoTemplate');
|
||||
var texts = $(s).find('p').map(function () {
|
||||
return $(this).text();
|
||||
}).get();
|
||||
|
||||
deepEqual(texts, ['0', '1', '2', '3', '4']);
|
||||
});
|
||||
|
||||
test('asynchronous', {
|
||||
asserts: 1
|
||||
}, function () {
|
||||
var d = $.Deferred();
|
||||
setTimeout(function () {
|
||||
ok(true);
|
||||
d.resolve();
|
||||
}, 100);
|
||||
return d;
|
||||
});
|
||||
test('unfail rejection', {
|
||||
asserts: 1,
|
||||
fail_on_rejection: false
|
||||
}, function () {
|
||||
var d = $.Deferred();
|
||||
setTimeout(function () {
|
||||
ok(true);
|
||||
d.reject();
|
||||
}, 100);
|
||||
return d;
|
||||
});
|
||||
|
||||
test('XML-RPC', {rpc: 'mock', asserts: 3}, function (instance, $s, mock) {
|
||||
mock('people.famous:name_search', function (args, kwargs) {
|
||||
strictEqual(kwargs.name, 'bob');
|
||||
return [
|
||||
[1, "Microsoft Bob"],
|
||||
[2, "Bob the Builder"],
|
||||
[3, "Silent Bob"]
|
||||
];
|
||||
});
|
||||
return new instance.web.Model('people.famous')
|
||||
.call('name_search', {name: 'bob'}).pipe(function (result) {
|
||||
strictEqual(result.length, 3, "shoud return 3 people");
|
||||
strictEqual(result[0][1], "Microsoft Bob",
|
||||
"the most famous bob should be Microsoft Bob");
|
||||
});
|
||||
});
|
||||
test('JSON-RPC', {rpc: 'mock', asserts: 3, templates: true}, function (instance, $s, mock) {
|
||||
var fetched_dbs = false, fetched_langs = false;
|
||||
mock('/web/database/get_list', function () {
|
||||
fetched_dbs = true;
|
||||
return ['foo', 'bar', 'baz'];
|
||||
});
|
||||
mock('/web/session/get_lang_list', function () {
|
||||
fetched_langs = true;
|
||||
return [['vo_IS', 'Hopelandic / Vonlenska']];
|
||||
});
|
||||
|
||||
// widget needs that or it blows up
|
||||
instance.webclient = {toggle_bars: openerp.testing.noop};
|
||||
var dbm = new instance.web.DatabaseManager({});
|
||||
return dbm.appendTo($s).pipe(function () {
|
||||
ok(fetched_dbs, "should have fetched databases");
|
||||
ok(fetched_langs, "should have fetched languages");
|
||||
deepEqual(dbm.db_list, ['foo', 'bar', 'baz']);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue