[MERGE] Web assets moved from manifests to ir.ui.view bundles
bzr revid: fme@openerp.com-20140430151302-e42monb4klz4udw5
This commit is contained in:
commit
acf2123123
|
@ -14,81 +14,7 @@ This module provides the core of the OpenERP Web Client.
|
|||
'data': [
|
||||
'views/webclient_templates.xml',
|
||||
],
|
||||
'js': [
|
||||
"static/lib/es5-shim/es5-shim.min.js",
|
||||
"static/lib/datejs/globalization/en-US.js",
|
||||
"static/lib/datejs/core.js",
|
||||
"static/lib/datejs/parser.js",
|
||||
"static/lib/datejs/sugarpak.js",
|
||||
"static/lib/datejs/extras.js",
|
||||
"static/lib/jquery/jquery.js",
|
||||
"static/lib/jquery.form/jquery.form.js",
|
||||
"static/lib/jquery.validate/jquery.validate.js",
|
||||
"static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
|
||||
"static/lib/spinjs/spin.js",
|
||||
"static/lib/jquery.autosize/jquery.autosize.js",
|
||||
"static/lib/jquery.blockUI/jquery.blockUI.js",
|
||||
"static/lib/jquery.hotkeys/jquery.hotkeys.js",
|
||||
"static/lib/jquery.placeholder/jquery.placeholder.js",
|
||||
"static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js",
|
||||
"static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js",
|
||||
"static/lib/jquery.ui.notify/js/jquery.notify.js",
|
||||
"static/lib/jquery.deferred-queue/jquery.deferred-queue.js",
|
||||
"static/lib/jquery.scrollTo/jquery.scrollTo-min.js",
|
||||
"static/lib/jquery.textext/jquery.textext.js",
|
||||
"static/lib/jquery.timeago/jquery.timeago.js",
|
||||
"static/lib/bootstrap/js/bootstrap.js",
|
||||
"static/lib/qweb/qweb2.js",
|
||||
"static/lib/underscore/underscore.js",
|
||||
"static/lib/underscore.string/lib/underscore.string.js",
|
||||
"static/lib/backbone/backbone.js",
|
||||
"static/lib/cleditor/jquery.cleditor.js",
|
||||
"static/lib/py.js/lib/py.js",
|
||||
"static/lib/select2/select2.js",
|
||||
"static/src/js/openerpframework.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/testing.js",
|
||||
"static/src/js/pyeval.js",
|
||||
"static/src/js/core.js",
|
||||
"static/src/js/formats.js",
|
||||
"static/src/js/chrome.js",
|
||||
"static/src/js/views.js",
|
||||
"static/src/js/data.js",
|
||||
"static/src/js/data_export.js",
|
||||
"static/src/js/search.js",
|
||||
"static/src/js/view_list.js",
|
||||
"static/src/js/view_form.js",
|
||||
"static/src/js/view_list_editable.js",
|
||||
"static/src/js/view_tree.js",
|
||||
],
|
||||
'css' : [
|
||||
"static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css",
|
||||
"static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
|
||||
"static/lib/jquery.ui.notify/css/ui.notify.css",
|
||||
"static/lib/jquery.textext/jquery.textext.css",
|
||||
"static/lib/fontawesome/css/font-awesome.css",
|
||||
"static/lib/bootstrap/css/bootstrap.css",
|
||||
"static/lib/select2/select2.css",
|
||||
"static/src/css/base.css",
|
||||
"static/src/css/data_export.css",
|
||||
"static/lib/cleditor/jquery.cleditor.css",
|
||||
],
|
||||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
],
|
||||
'test': [
|
||||
"static/test/testing.js",
|
||||
"static/test/framework.js",
|
||||
"static/test/registry.js",
|
||||
"static/test/form.js",
|
||||
"static/test/data.js",
|
||||
"static/test/list-utils.js",
|
||||
"static/test/formats.js",
|
||||
"static/test/rpc-misordered.js",
|
||||
"static/test/evals.js",
|
||||
"static/test/search.js",
|
||||
"static/test/list.js",
|
||||
"static/test/list-editable.js",
|
||||
"static/test/mutex.js"
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
from . import main
|
||||
from . import testing
|
||||
|
|
|
@ -31,6 +31,7 @@ except ImportError:
|
|||
|
||||
import openerp
|
||||
import openerp.modules.registry
|
||||
from openerp.addons.base.ir.ir_qweb import AssetsBundle, QWebTemplateNotFound
|
||||
from openerp.tools.translate import _
|
||||
from openerp import http
|
||||
|
||||
|
@ -52,50 +53,6 @@ env.filters["json"] = simplejson.dumps
|
|||
# OpenERP Web helpers
|
||||
#----------------------------------------------------------
|
||||
|
||||
def rjsmin(script):
|
||||
""" Minify js with a clever regex.
|
||||
Taken from http://opensource.perlig.de/rjsmin
|
||||
Apache License, Version 2.0 """
|
||||
def subber(match):
|
||||
""" Substitution callback """
|
||||
groups = match.groups()
|
||||
return (
|
||||
groups[0] or
|
||||
groups[1] or
|
||||
groups[2] or
|
||||
groups[3] or
|
||||
(groups[4] and '\n') or
|
||||
(groups[5] and ' ') or
|
||||
(groups[6] and ' ') or
|
||||
(groups[7] and ' ') or
|
||||
''
|
||||
)
|
||||
|
||||
result = re.sub(
|
||||
r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?'
|
||||
r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|'
|
||||
r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?:(?<=[(,=:\[!&|?{};\r\n]'
|
||||
r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/'
|
||||
r'))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*'
|
||||
r'(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*'
|
||||
r'))|(?:(?<=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\01'
|
||||
r'6-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*((?:/(?![\r\n/*])[^/'
|
||||
r'\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]'
|
||||
r'*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*))|(?<=[^\000-!#%&(*,./'
|
||||
r':-@\[\\^`{|~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/'
|
||||
r'*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\01'
|
||||
r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#'
|
||||
r'%-\047)*,./:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-'
|
||||
r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^'
|
||||
r'\000-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|'
|
||||
r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\0'
|
||||
r'13\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\0'
|
||||
r'00-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:'
|
||||
r'(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*'
|
||||
r']*\*+(?:[^/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script
|
||||
).strip()
|
||||
return result
|
||||
|
||||
db_list = http.db_list
|
||||
|
||||
db_monodb = http.db_monodb
|
||||
|
@ -309,45 +266,6 @@ def concat_xml(file_list):
|
|||
root.append(child)
|
||||
return ElementTree.tostring(root, 'utf-8'), checksum.hexdigest()
|
||||
|
||||
def concat_files(file_list, reader=None, intersperse=""):
|
||||
""" Concatenates contents of all provided files
|
||||
|
||||
:param list(str) file_list: list of files to check
|
||||
:param function reader: reading procedure for each file
|
||||
:param str intersperse: string to intersperse between file contents
|
||||
:returns: (concatenation_result, checksum)
|
||||
:rtype: (str, str)
|
||||
"""
|
||||
checksum = hashlib.new('sha1')
|
||||
if not file_list:
|
||||
return '', checksum.hexdigest()
|
||||
|
||||
if reader is None:
|
||||
def reader(f):
|
||||
import codecs
|
||||
with codecs.open(f, 'rb', "utf-8-sig") as fp:
|
||||
return fp.read().encode("utf-8")
|
||||
|
||||
files_content = []
|
||||
for fname in file_list:
|
||||
contents = reader(fname)
|
||||
checksum.update(contents)
|
||||
files_content.append(contents)
|
||||
|
||||
files_concat = intersperse.join(files_content)
|
||||
return files_concat, checksum.hexdigest()
|
||||
|
||||
concat_js_cache = {}
|
||||
|
||||
def concat_js(file_list):
|
||||
content, checksum = concat_files(file_list, intersperse=';')
|
||||
if checksum in concat_js_cache:
|
||||
content = concat_js_cache[checksum]
|
||||
else:
|
||||
content = rjsmin(content)
|
||||
concat_js_cache[checksum] = content
|
||||
return content, checksum
|
||||
|
||||
def fs2web(path):
|
||||
"""convert FS path into web path"""
|
||||
return '/'.join(path.split(os.path.sep))
|
||||
|
@ -371,30 +289,17 @@ def manifest_glob(extension, addons=None, db=None, include_remotes=False):
|
|||
r.append((None, pattern))
|
||||
else:
|
||||
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
|
||||
# Hack for IE, who limit 288Ko, 4095 rules, 31 sheets
|
||||
# http://support.microsoft.com/kb/262161/en
|
||||
if pattern == "static/lib/bootstrap/css/bootstrap.css":
|
||||
if include_remotes:
|
||||
r.insert(0, (None, fs2web(path[len(addons_path):])))
|
||||
else:
|
||||
r.append((path, fs2web(path[len(addons_path):])))
|
||||
r.append((path, fs2web(path[len(addons_path):])))
|
||||
return r
|
||||
|
||||
def manifest_list(extension, mods=None, db=None, debug=False):
|
||||
def manifest_list(extension, mods=None, db=None, debug=None):
|
||||
""" list ressources to load specifying either:
|
||||
mods: a comma separated string listing modules
|
||||
db: a database name (return all installed modules in that database)
|
||||
"""
|
||||
if debug is not None:
|
||||
_logger.warning("openerp.addons.web.main.manifest_list(): debug parameter is deprecated")
|
||||
files = manifest_glob(extension, addons=mods, db=db, include_remotes=True)
|
||||
if not debug:
|
||||
path = '/web/webclient/' + extension
|
||||
if mods is not None:
|
||||
path += '?' + werkzeug.url_encode({'mods': mods})
|
||||
elif db:
|
||||
path += '?' + werkzeug.url_encode({'db': db})
|
||||
|
||||
remotes = [wp for fp, wp in files if fp is None]
|
||||
return [path] + remotes
|
||||
return [wp for _fp, wp in files]
|
||||
|
||||
def get_last_modified(files):
|
||||
|
@ -411,7 +316,7 @@ def get_last_modified(files):
|
|||
for f in files)
|
||||
return datetime.datetime(1970, 1, 1)
|
||||
|
||||
def make_conditional(response, last_modified=None, etag=None):
|
||||
def make_conditional(response, last_modified=None, etag=None, max_age=0):
|
||||
""" Makes the provided response conditional based upon the request,
|
||||
and mandates revalidation from clients
|
||||
|
||||
|
@ -426,7 +331,7 @@ def make_conditional(response, last_modified=None, etag=None):
|
|||
:rtype: werkzeug.wrappers.Response
|
||||
"""
|
||||
response.cache_control.must_revalidate = True
|
||||
response.cache_control.max_age = 0
|
||||
response.cache_control.max_age = max_age
|
||||
if last_modified:
|
||||
response.last_modified = last_modified
|
||||
if etag:
|
||||
|
@ -603,6 +508,10 @@ html_template = """<!DOCTYPE html>
|
|||
<title>OpenERP</title>
|
||||
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="/web/static/src/css/full.css" />
|
||||
|
||||
<link rel="stylesheet" href="/web/css/web.assets_backend"/>
|
||||
<script type="text/javascript" src="/web/js/web.assets_backend"></script>
|
||||
|
||||
%(css)s
|
||||
%(js)s
|
||||
<script type="text/javascript">
|
||||
|
@ -621,26 +530,16 @@ html_template = """<!DOCTYPE html>
|
|||
</html>
|
||||
"""
|
||||
|
||||
def render_bootstrap_template(template, values=None, debug=False, db=None, **kw):
|
||||
if not db:
|
||||
db = request.db
|
||||
if request.debug:
|
||||
debug = True
|
||||
def render_bootstrap_template(template, values=None, **kw):
|
||||
if values is None:
|
||||
values = {}
|
||||
values['debug'] = debug
|
||||
values['current_db'] = db
|
||||
values = dict()
|
||||
try:
|
||||
values['databases'] = http.db_list()
|
||||
except openerp.exceptions.AccessDenied:
|
||||
values['databases'] = None
|
||||
|
||||
for res in ['js', 'css']:
|
||||
if res not in values:
|
||||
values[res] = manifest_list(res, db=db, debug=debug)
|
||||
|
||||
if 'modules' not in values:
|
||||
values['modules'] = module_boot(db=db)
|
||||
values['modules'] = module_boot()
|
||||
values['modules'] = simplejson.dumps(values['modules'])
|
||||
|
||||
return request.render(template, values, **kw)
|
||||
|
@ -692,6 +591,38 @@ class Home(http.Controller):
|
|||
def login(self, db, login, key, redirect="/web", **kw):
|
||||
return login_and_redirect(db, login, key, redirect_url=redirect)
|
||||
|
||||
@http.route('/web/js/<xmlid>', type='http', auth="public")
|
||||
def js_bundle(self, xmlid, **kw):
|
||||
# manifest backward compatible mode, to be removed
|
||||
values = {'manifest_list': manifest_list}
|
||||
try:
|
||||
assets_html = request.render(xmlid, lazy=False, qcontext=values)
|
||||
except QWebTemplateNotFound:
|
||||
return request.not_found()
|
||||
bundle = AssetsBundle(xmlid, assets_html, debug=request.debug)
|
||||
|
||||
response = request.make_response(
|
||||
bundle.js(), [('Content-Type', 'application/javascript')])
|
||||
|
||||
# TODO: check that we don't do weird lazy overriding of __call__ which break body-removal
|
||||
return make_conditional(
|
||||
response, bundle.last_modified, bundle.checksum, max_age=60*5)
|
||||
|
||||
@http.route('/web/css/<xmlid>', type='http', auth='public')
|
||||
def css_bundle(self, xmlid, **kw):
|
||||
values = {'manifest_list': manifest_list} # manifest backward compatible mode, to be removed
|
||||
try:
|
||||
assets_html = request.render(xmlid, lazy=False, qcontext=values)
|
||||
except QWebTemplateNotFound:
|
||||
return request.not_found()
|
||||
bundle = AssetsBundle(xmlid, assets_html, debug=request.debug)
|
||||
|
||||
response = request.make_response(
|
||||
bundle.css(), [('Content-Type', 'text/css')])
|
||||
|
||||
return make_conditional(
|
||||
response, bundle.last_modified, bundle.checksum, max_age=60*5)
|
||||
|
||||
class WebClient(http.Controller):
|
||||
|
||||
@http.route('/web/webclient/csslist', type='json', auth="none")
|
||||
|
@ -706,70 +637,6 @@ class WebClient(http.Controller):
|
|||
def qweblist(self, mods=None):
|
||||
return manifest_list('qweb', mods=mods)
|
||||
|
||||
@http.route('/web/webclient/css', type='http', auth="none")
|
||||
def css(self, mods=None, db=None):
|
||||
files = list(manifest_glob('css', addons=mods, db=db))
|
||||
last_modified = get_last_modified(f[0] for f in files)
|
||||
if request.httprequest.if_modified_since and request.httprequest.if_modified_since >= last_modified:
|
||||
return werkzeug.wrappers.Response(status=304)
|
||||
|
||||
file_map = dict(files)
|
||||
|
||||
rx_import = re.compile(r"""@import\s+('|")(?!'|"|/|https?://)""", re.U)
|
||||
rx_url = re.compile(r"""url\s*\(\s*('|"|)(?!'|"|/|https?://|data:)""", re.U)
|
||||
|
||||
def reader(f):
|
||||
"""read the a css file and absolutify all relative uris"""
|
||||
with open(f, 'rb') as fp:
|
||||
data = fp.read().decode('utf-8')
|
||||
|
||||
path = file_map[f]
|
||||
web_dir = os.path.dirname(path)
|
||||
|
||||
data = re.sub(
|
||||
rx_import,
|
||||
r"""@import \1%s/""" % (web_dir,),
|
||||
data,
|
||||
)
|
||||
|
||||
data = re.sub(
|
||||
rx_url,
|
||||
r"url(\1%s/" % (web_dir,),
|
||||
data,
|
||||
)
|
||||
return data.encode('utf-8')
|
||||
|
||||
content, checksum = concat_files((f[0] for f in files), reader)
|
||||
|
||||
# move up all @import and @charset rules to the top
|
||||
matches = []
|
||||
def push(matchobj):
|
||||
matches.append(matchobj.group(0))
|
||||
return ''
|
||||
|
||||
content = re.sub(re.compile("(@charset.+;$)", re.M), push, content)
|
||||
content = re.sub(re.compile("(@import.+;$)", re.M), push, content)
|
||||
|
||||
matches.append(content)
|
||||
content = '\n'.join(matches)
|
||||
|
||||
return make_conditional(
|
||||
request.make_response(content, [('Content-Type', 'text/css')]),
|
||||
last_modified, checksum)
|
||||
|
||||
@http.route('/web/webclient/js', type='http', auth="none")
|
||||
def js(self, mods=None, db=None):
|
||||
files = [f[0] for f in manifest_glob('js', addons=mods, db=db)]
|
||||
last_modified = get_last_modified(files)
|
||||
if request.httprequest.if_modified_since and request.httprequest.if_modified_since >= last_modified:
|
||||
return werkzeug.wrappers.Response(status=304)
|
||||
|
||||
content, checksum = concat_js(files)
|
||||
|
||||
return make_conditional(
|
||||
request.make_response(content, [('Content-Type', 'application/javascript')]),
|
||||
last_modified, checksum)
|
||||
|
||||
@http.route('/web/webclient/qweb', type='http', auth="none")
|
||||
def qweb(self, mods=None, db=None):
|
||||
files = [f[0] for f in manifest_glob('qweb', addons=mods, db=db)]
|
||||
|
@ -843,6 +710,10 @@ class WebClient(http.Controller):
|
|||
def version_info(self):
|
||||
return openerp.service.common.exp_version()
|
||||
|
||||
@http.route('/web/tests', type='http', auth="none")
|
||||
def index(self, mod=None, **kwargs):
|
||||
return request.render('web.qunit_suite')
|
||||
|
||||
class Proxy(http.Controller):
|
||||
|
||||
@http.route('/web/proxy/load', type='json', auth="none")
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
# 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 openerp import http
|
||||
from openerp.http import request
|
||||
|
||||
from .main import module_topological_sort
|
||||
|
||||
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>
|
||||
""", default_filters=['h'])
|
||||
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>
|
||||
""", default_filters=['h'])
|
||||
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 | n};
|
||||
QUnit.config.testTimeout = 5 * 60 * 1000;
|
||||
</script>
|
||||
</head>
|
||||
<body id="oe" class="openerp">
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</body>
|
||||
<!-- TODO xmo please use the regular template even for testing -->
|
||||
% for module, jss, tests, templates in files:
|
||||
% for js in jss:
|
||||
% if not js.endswith('/apps.js'):
|
||||
<script src="${to_path(module, js)}"></script>
|
||||
% endif
|
||||
% 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>
|
||||
""", default_filters=['h'])
|
||||
|
||||
class TestRunnerController(http.Controller):
|
||||
|
||||
@http.route('/web/tests', type='http', auth="none")
|
||||
def index(self, 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 request.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, '/')
|
||||
|
|
@ -1457,7 +1457,7 @@ instance.web.embed = function (origin, dbname, login, key, action, options) {
|
|||
$('head').append($('<link>', {
|
||||
'rel': 'stylesheet',
|
||||
'type': 'text/css',
|
||||
'href': origin +'/web/webclient/css'
|
||||
'href': origin +'/web/css/web.assets_webclient'
|
||||
}));
|
||||
var currentScript = document.currentScript;
|
||||
if (!currentScript) {
|
||||
|
|
|
@ -48,15 +48,6 @@ openerp.testing = {};
|
|||
|
||||
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
|
||||
*/
|
||||
|
@ -206,7 +197,7 @@ openerp.testing = {};
|
|||
teardown: testing.noop
|
||||
});
|
||||
|
||||
QUnit.module(testing.current_module + '.' + name, {_oe: options});
|
||||
QUnit.module(name, {_oe: options});
|
||||
body(testing['case']);
|
||||
};
|
||||
testing['case'] = function (name, options, callback) {
|
||||
|
@ -244,18 +235,6 @@ openerp.testing = {};
|
|||
expect(opts.asserts);
|
||||
}
|
||||
|
||||
if (opts.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 $fixture = $('#qunit-fixture');
|
||||
|
||||
var mock, async = false;
|
||||
|
|
|
@ -361,8 +361,8 @@ openerp.testing.section('list.edition.onwrite', {
|
|||
strictEqual(
|
||||
$fix.find('tbody tr:eq(1)').css('color'), 'rgb(255, 0, 0)',
|
||||
'shoud have color applied');
|
||||
strictEqual(
|
||||
$fix.find('tbody tr:eq(2)').css('color'), 'rgb(0, 0, 0)',
|
||||
notStrictEqual(
|
||||
$fix.find('tbody tr:eq(2)').css('color'), 'rgb(255, 0, 0)',
|
||||
'should have default color applied');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
import test_js
|
||||
import test_menu
|
||||
import test_serving_base
|
||||
import test_ui
|
||||
|
|
|
@ -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/
|
|
@ -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);
|
|
@ -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.
|
|
@ -1 +0,0 @@
|
|||
{}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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 '<QUnitTest %s:%s>' % (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
|
|
@ -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
|
||||
|
|
|
@ -3,6 +3,101 @@
|
|||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="web.assets_common">
|
||||
<script type="text/javascript" src="/web/static/lib/es5-shim/es5-shim.min.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/datejs/globalization/en-US.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/spinjs/spin.js"></script>
|
||||
|
||||
<!-- jQuery stuff -->
|
||||
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.blockUI/jquery.blockUI.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.hotkeys/jquery.hotkeys.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.placeholder/jquery.placeholder.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.timeago/jquery.timeago.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.form/jquery.form.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
|
||||
</template>
|
||||
|
||||
<template id="web.assets_backend">
|
||||
<t t-call="web.assets_common"/>
|
||||
<!-- Datejs -->
|
||||
<script type="text/javascript" src="/web/static/lib/datejs/core.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/datejs/parser.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/datejs/sugarpak.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/datejs/extras.js"></script>
|
||||
|
||||
<!-- jQuery addons -->
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.validate/jquery.validate.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.autosize/jquery.autosize.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/cleditor/jquery.cleditor.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/cleditor/jquery.cleditor.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/jquery.textext/jquery.textext.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.textext/jquery.textext.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/select2/select2.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/select2/select2.js"></script>
|
||||
|
||||
|
||||
<!-- jQuery ui -->
|
||||
<link rel="stylesheet" href="/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/web/static/lib/jquery.ui.notify/css/ui.notify.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui.notify/js/jquery.notify.js"></script>
|
||||
|
||||
<!-- Bootstrap must be loaded after jquery.ui until jquery.ui is removed -->
|
||||
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
|
||||
<script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script>
|
||||
|
||||
<!-- Backbone -->
|
||||
<script type="text/javascript" src="/web/static/lib/backbone/backbone.js"></script>
|
||||
|
||||
<!-- Internals -->
|
||||
<link rel="stylesheet" href="/web/static/src/css/base.css"/>
|
||||
<link rel="stylesheet" href="/web/static/src/css/data_export.css"/>
|
||||
<link rel="stylesheet" href="/base/static/src/css/modules.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/py.js/lib/py.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/boot.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/testing.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/pyeval.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/core.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/formats.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/chrome.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/data.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/data_export.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/search.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/view_list.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/view_form.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/view_list_editable.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/view_tree.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/apps.js"></script>
|
||||
|
||||
</template>
|
||||
|
||||
<template id="web.assets_webclient_manifest">
|
||||
<!-- This bundle can be used for module manifest asset declaration backward compatibility -->
|
||||
<t t-foreach="manifest_list('css')" t-as="css_file">
|
||||
<link rel="stylesheet" t-att-href="css_file"/>
|
||||
</t>
|
||||
<t t-foreach="manifest_list('js')" t-as="js_file">
|
||||
<script type="text/javascript" t-att-src="js_file"></script>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="web.layout" name="Web layout"><!DOCTYPE html>
|
||||
<html style="height: 100%">
|
||||
|
@ -23,12 +118,7 @@
|
|||
<template id="web.webclient_bootstrap" name="Webclient Bootstrap">
|
||||
<t t-call="web.layout">
|
||||
<t t-set="head">
|
||||
<t t-foreach="css" t-as="css_file">
|
||||
<link rel="stylesheet" t-att-href="css_file"/>
|
||||
</t>
|
||||
<t t-foreach="js" t-as="js_file">
|
||||
<script type="text/javascript" t-att-src="js_file"></script>
|
||||
</t>
|
||||
<t t-call-assets="web.assets_backend"/>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var s = new openerp.init(<t t-raw="modules"/>);
|
||||
|
@ -78,10 +168,10 @@
|
|||
</script>
|
||||
<div class="form-group field-db" t-if="databases and len(databases) > 1">
|
||||
<label for="db" class="control-label">Database</label>
|
||||
<select name="db" id="db" class="form-control" required="required" t-att-autofocus="'autofocus' if current_db not in databases else None" onchange="dbchanged(this.value)">
|
||||
<select name="db" id="db" class="form-control" required="required" t-att-autofocus="'autofocus' if request.db not in databases else None" onchange="dbchanged(this.value)">
|
||||
<option></option>
|
||||
<t t-foreach="databases" t-as="db">
|
||||
<option t-att-selected="'selected' if db == current_db else None">
|
||||
<option t-att-selected="'selected' if db == request.db else None">
|
||||
<t t-esc="db"/>
|
||||
</option>
|
||||
</t>
|
||||
|
@ -121,5 +211,55 @@
|
|||
</t>
|
||||
</template>
|
||||
|
||||
<template id="web.qunit_suite">
|
||||
<!DOCTYPE html>
|
||||
<html style="height: 100%">
|
||||
<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>
|
||||
|
||||
<t t-call="web.assets_backend"/>
|
||||
|
||||
<script type="text/javascript" id="qunit_config">
|
||||
QUnit.config.testTimeout = 5 * 60 * 1000;
|
||||
QUnit.moduleDone(function(result) {
|
||||
console.log(result.name + " (" + result.passed + "/" + result.total + " passed tests)");
|
||||
});
|
||||
QUnit.done(function(result) {
|
||||
if (result.failed === 0) {
|
||||
console.log('ok');
|
||||
}
|
||||
});
|
||||
openerp.web.qweb.add_template("/web/webclient/qweb");
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/test/testing.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/framework.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/data.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-misordered.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/list.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/list-editable.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/mutex.js"></script>
|
||||
</head>
|
||||
|
||||
<body id="oe" class="openerp">
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -10,8 +10,4 @@ Openerp Web API.
|
|||
'depends': ['web'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'js' : [
|
||||
],
|
||||
'css' : [
|
||||
],
|
||||
}
|
||||
|
|
|
@ -9,14 +9,8 @@ OpenERP Web Calendar view.
|
|||
'author': 'OpenERP SA, Valentino Lab (Kalysto)',
|
||||
'version': '2.0',
|
||||
'depends': ['web'],
|
||||
'data' : [],
|
||||
'js': [
|
||||
'static/lib/fullcalendar/js/fullcalendar.js',
|
||||
'static/src/js/*.js'
|
||||
],
|
||||
'css': [
|
||||
'static/lib/fullcalendar/css/*.css',
|
||||
'static/src/css/*.css'
|
||||
'data' : [
|
||||
'views/web_calendar.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/*.xml',
|
||||
|
|
|
@ -11,7 +11,6 @@ _.str.toBoolElse = function (str, elseValues, trueValues, falseValues) {
|
|||
};
|
||||
|
||||
openerp.web_calendar = function(instance) {
|
||||
|
||||
var _t = instance.web._t,
|
||||
_lt = instance.web._lt,
|
||||
QWeb = instance.web.qweb;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_calendar assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_calendar/static/src/css/web_fullcalendar.css"/>
|
||||
<link rel="stylesheet" href="/web_calendar/static/lib/fullcalendar/css/fullcalendar.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_calendar/static/lib/fullcalendar/js/fullcalendar.js"></script>
|
||||
<script type="text/javascript" src="/web_calendar/static/src/js/web_calendar.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -8,15 +8,8 @@ Openerp Web Diagram view.
|
|||
""",
|
||||
'version': '2.0',
|
||||
'depends': ['web'],
|
||||
'js': [
|
||||
'static/lib/js/raphael.js',
|
||||
'static/lib/js/jquery.mousewheel.js',
|
||||
'static/src/js/vec2.js',
|
||||
'static/src/js/graph.js',
|
||||
'static/src/js/diagram.js',
|
||||
],
|
||||
'css': [
|
||||
'static/src/css/base_diagram.css',
|
||||
'data' : [
|
||||
'views/web_diagram.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/*.xml',
|
||||
|
|
|
@ -24,6 +24,15 @@ instance.web.DiagramView = instance.web.View.extend({
|
|||
this.ids = this.dataset.ids;
|
||||
this.on('pager_action_executed', self, self.pager_action_trigger);
|
||||
},
|
||||
start: function () {
|
||||
return this._super().then(function () {
|
||||
return $.when(
|
||||
openerp.webclient.session.load_js(['/web/js/web_diagram.assets_raphael'])
|
||||
);
|
||||
}).fail(function () {
|
||||
throw new Error("Could not load raphael.js");
|
||||
});
|
||||
},
|
||||
|
||||
view_loading: function(r) {
|
||||
return this.load_diagram(r);
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_diagram assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_diagram/static/src/css/base_diagram.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_diagram/static/lib/js/jquery.mousewheel.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web_diagram/static/src/js/vec2.js"></script>
|
||||
<script type="text/javascript" src="/web_diagram/static/src/js/graph.js"></script>
|
||||
<script type="text/javascript" src="/web_diagram/static/src/js/diagram.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="assets_raphael" name="raphael.js">
|
||||
<script type="text/javascript" src="/web_diagram/static/lib/js/raphael.js"></script>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -8,12 +8,9 @@ OpenERP Web Gantt chart view.
|
|||
""",
|
||||
'version': '2.0',
|
||||
'depends': ['web'],
|
||||
'js': [
|
||||
'static/lib/dhtmlxGantt/sources/dhtmlxcommon.js',
|
||||
'static/lib/dhtmlxGantt/sources/dhtmlxgantt.js',
|
||||
'static/src/js/gantt.js'
|
||||
'data' : [
|
||||
'views/web_gantt.xml',
|
||||
],
|
||||
'css': ['static/src/css/gantt.css', 'static/lib/dhtmlxGantt/codebase/dhtmlxgantt.css'],
|
||||
'qweb': [
|
||||
'static/src/xml/*.xml',
|
||||
],
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_gantt assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_gantt/static/src/css/gantt.css"/>
|
||||
<link rel="stylesheet" href="/web_gantt/static/lib/dhtmlxGantt/codebase/dhtmlxgantt.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_gantt/static/lib/dhtmlxGantt/sources/dhtmlxcommon.js"></script>
|
||||
<script type="text/javascript" src="/web_gantt/static/lib/dhtmlxGantt/sources/dhtmlxgantt.js"></script>
|
||||
<script type="text/javascript" src="/web_gantt/static/src/js/gantt.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -14,15 +14,8 @@ Graph Views for Web Client.
|
|||
""",
|
||||
'version': '3.0',
|
||||
'depends': ['web'],
|
||||
'js': [
|
||||
'static/lib/nvd3/d3.v3.js',
|
||||
'static/lib/nvd3/nv.d3.js',
|
||||
'static/src/js/graph_view.js',
|
||||
'static/src/js/pivot_table.js',
|
||||
'static/src/js/graph_widget.js',
|
||||
],
|
||||
'css': [
|
||||
'static/src/css/*.css',
|
||||
'data' : [
|
||||
'views/web_graph.xml',
|
||||
],
|
||||
'qweb' : [
|
||||
'static/src/xml/*.xml',
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_graph assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_graph/static/src/css/graph.css"/>
|
||||
<link rel="stylesheet" href="/web_graph/static/src/css/nv.d3.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_graph/static/lib/nvd3/d3.v3.js"></script>
|
||||
<script type="text/javascript" src="/web_graph/static/lib/nvd3/nv.d3.js"></script>
|
||||
<script type="text/javascript" src="/web_graph/static/src/js/graph_view.js"></script>
|
||||
<script type="text/javascript" src="/web_graph/static/src/js/pivot_table.js"></script>
|
||||
<script type="text/javascript" src="/web_graph/static/src/js/graph_widget.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -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,
|
||||
}
|
|
@ -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:
|
|
@ -8,11 +8,8 @@ OpenERP Web kanban view.
|
|||
""",
|
||||
'version': '2.0',
|
||||
'depends': ['web'],
|
||||
'js': [
|
||||
'static/src/js/kanban.js'
|
||||
],
|
||||
'css': [
|
||||
'static/src/css/kanban.css'
|
||||
'data' : [
|
||||
'views/web_kanban.xml',
|
||||
],
|
||||
'qweb' : [
|
||||
'static/src/xml/*.xml',
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_kanban assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_kanban/static/src/css/kanban.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_kanban/static/src/js/kanban.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -6,11 +6,8 @@ This widget allows to display gauges using justgage library.
|
|||
""",
|
||||
'version': '1.0',
|
||||
'depends': ['web_kanban'],
|
||||
'js': [
|
||||
'static/lib/justgage/justgage.js',
|
||||
'static/src/js/kanban_gauge.js'
|
||||
],
|
||||
'css': [
|
||||
'data' : [
|
||||
'views/web_kanban_gauge.xml',
|
||||
],
|
||||
'qweb': [
|
||||
],
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_kanban_gauge assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/web_kanban_gauge/static/lib/justgage/justgage.js"></script>
|
||||
<script type="text/javascript" src="/web_kanban_gauge/static/src/js/kanban_gauge.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -6,11 +6,8 @@ This widget allows to display sparklines using jquery.sparkline library.
|
|||
""",
|
||||
'version': '1.0',
|
||||
'depends': ['web_kanban'],
|
||||
'js': [
|
||||
"static/lib/jquery.sparkline/jquery.sparkline.js",
|
||||
'static/src/js/kanban_sparkline.js'
|
||||
],
|
||||
'css': [
|
||||
'data' : [
|
||||
'views/web_kanban_sparkline.xml',
|
||||
],
|
||||
'qweb': [
|
||||
],
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_kanban_sparkline assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/web_kanban_sparkline/static/lib/jquery.sparkline/jquery.sparkline.js"></script>
|
||||
<script type="text/javascript" src="/web_kanban_sparkline/static/src/js/kanban_sparkline.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -1 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import tests
|
||||
|
|
|
@ -7,8 +7,9 @@ OpenERP Web test suite.
|
|||
|
||||
""",
|
||||
'version': '2.0',
|
||||
'depends': [],
|
||||
'js': ['static/src/js/*.js'],
|
||||
'css': ['static/src/css/*.css'],
|
||||
'depends': ['web', 'web_kanban'],
|
||||
'data' : [
|
||||
'views/web_tests.xml',
|
||||
],
|
||||
'auto_install': True,
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import test_js
|
||||
import test_ui
|
|
@ -11,6 +11,6 @@ class TestUi(openerp.tests.HttpCase):
|
|||
def test_03_js_public(self):
|
||||
self.phantom_js('/',"console.log('ok')","console")
|
||||
def test_04_js_admin(self):
|
||||
self.phantom_js('/',"console.log('ok')","openerp.client.action_manager.inner_widget.views.form", login='admin')
|
||||
self.phantom_js('/web',"console.log('ok')","openerp.client.action_manager.inner_widget.views.form", login='admin')
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_tests assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_tests/static/src/css/web_tests.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_tests/static/src/js/web_tests.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -8,7 +8,8 @@ OpenERP Web demo of a test suite
|
|||
Test suite example, same code as that used in the testing documentation.
|
||||
""",
|
||||
'depends': ['web'],
|
||||
'js': ['static/src/js/demo.js'],
|
||||
'test': ['static/test/demo.js'],
|
||||
'data' : [
|
||||
'views/web_tests_demo.xml',
|
||||
],
|
||||
'qweb': ['static/src/xml/demo.xml'],
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// static/src/js/demo.js
|
||||
openerp.web_tests_demo = function (instance) {
|
||||
_.extend(instance.web_tests_demo, {
|
||||
(function () {
|
||||
openerp.web_tests_demo = {
|
||||
value_true: true,
|
||||
SomeType: instance.web.Class.extend({
|
||||
SomeType: openerp.web.Class.extend({
|
||||
init: function (value) {
|
||||
this.value = value;
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
}());
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_tests_demo assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/web_tests_demo/static/src/js/demo.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="qunit_suite" name="web_tests_demo qunit" inherit_id="web.qunit_suite">
|
||||
<xpath expr="//head" position="inside">
|
||||
<script type="text/javascript" src="/web_tests_demo/static/test/demo.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -8,8 +8,9 @@ OpenERP Web to edit views.
|
|||
""",
|
||||
'version': '2.0',
|
||||
'depends':['web'],
|
||||
'js': ['static/src/js/view_editor.js'],
|
||||
'css': ['static/src/css/view_editor.css'],
|
||||
'data' : [
|
||||
'views/web_view_editor.xml',
|
||||
],
|
||||
'qweb': ['static/src/xml/view_editor.xml'],
|
||||
'auto_install': True,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="web_view_editor assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/web_view_editor/static/src/css/view_editor.css"/>
|
||||
|
||||
<script type="text/javascript" src="/web_view_editor/static/src/js/view_editor.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
Loading…
Reference in New Issue