[IMP] *: new asset for test files, new debug mode (stored in session)
This commit goal is to encapsulate every tour-test into a separate assets bundle that would be called only during tests (command line) or by URL (debug=tests). That way, a lot of .js files would not be loaded anymore uselessly outside test mode and will speed up the page loads (especially in the frontend as the backend do not reload the page anyway). In order to do that, we needed to propagate the `debug` state from page to page. Otherwise, every page change during a test would simply lose the debug mode and the test would stop as the test assets would not be loaded. As we could not add the `&debug=tests` on every link (either hardcoded or preprocess during the rendering), it has been decided to store it in session. Technical summary: 1. `debug` state (currently only stored in URL) will be stored in session. Either when adding the `debug` param in URL (handled with _dispatch) or by starting Odoo with `test-enable` or `test-file` (handled by session init). 2. Once activated (and so set in session), debug mode will remain activated even if not visible in URL (after a page navigation eg). To deactivate it, set its value to nothing, `debug=`. That will exit debug mode (whatever mode it is: debug, assets, tests). 3. As tour-test files are now in a separate bundle, every layout (when needed) should `t-call="web.conditional_assets_tests"`. As it would be redundant and verbose, no `t-if` is needed on the t-call to load it only in test debug mode. That will be handled by `compiled_assets_tests` that will actually do the conditionnal t-call-assets to web.assets_tests, if tests debug mode is activated. 4. In addition to separating the tour-tests files in a separate bundle, we also moved those files to a specific folder under /static/tests/tours next to QUnit tests. 5. Also, tour files will be moved in a specific folder /static/src/js/tours for cleanness purpose (those files will still be kept in their 'normal' assets as needed outside test mode since it is tours). This will be done in the next commit. 6. It is possible to enable multiple debug mode, such as 'tests' and 'assets' together. Simply separate debug modes with a comma, eg '?debug=assets,tests' task-1934445 Comes with https://github.com/odoo/enterprise/pull/4281 Closes #33213
This commit is contained in:
committed by
Jeremy Kersten
parent
2fab076e84
commit
d7cd97a9be
@@ -101,7 +101,7 @@ class Proxy(http.Controller):
|
||||
<p>The list of connected USB devices as seen by the IoTBox</p>
|
||||
"""
|
||||
if debug is None:
|
||||
resp += """(<a href="/hw_proxy/status?debug">debug version</a>)"""
|
||||
resp += """(<a href="/hw_proxy/status?debug=1">debug version</a>)"""
|
||||
devices = subprocess.check_output("lsusb").decode('utf-8').split('\n')
|
||||
count = 0
|
||||
resp += "<div class='devices'>\n"
|
||||
|
||||
@@ -12,7 +12,7 @@ _logger = logging.getLogger(__name__)
|
||||
class PosController(http.Controller):
|
||||
|
||||
@http.route('/pos/web', type='http', auth='user')
|
||||
def pos_web(self, debug=False, **k):
|
||||
def pos_web(self, **k):
|
||||
# if user not logged in, log him in
|
||||
pos_sessions = request.env['pos.session'].search([
|
||||
('state', '=', 'opened'),
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
var odoo = {
|
||||
csrf_token: "<t t-esc="request.csrf_token(None)"/>",
|
||||
session_info: <t t-raw="session_info"/>,
|
||||
debug: "<t t-esc="debug"/>",
|
||||
};
|
||||
</script>
|
||||
|
||||
<t t-call-assets="web.assets_common" t-css="false"/>
|
||||
<t t-call-assets="point_of_sale.pos_assets_backend" t-css="false"/>
|
||||
<t t-call-assets="point_of_sale.assets"/>
|
||||
<t t-call="web.conditional_assets_tests"/>
|
||||
|
||||
<script type="text/javascript" id="loading-script" t-raw="init">
|
||||
odoo.define('web.web_client', function (require) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import json
|
||||
class BarcodeController(http.Controller):
|
||||
|
||||
@http.route(['/stock/barcode/'], type='http', auth='user')
|
||||
def a(self, debug=False, **k):
|
||||
def a(self, **k):
|
||||
if not request.session.uid:
|
||||
return http.local_redirect('/web/login?redirect=/stock/barcode/')
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ var tour = require('web_tour.tour');
|
||||
|
||||
tour.register('stock', {
|
||||
test: true,
|
||||
url: '/web?debug=assets#action=stock.action_stock_config_settings',
|
||||
url: '/web#action=stock.action_stock_config_settings',
|
||||
},
|
||||
[
|
||||
{
|
||||
|
||||
@@ -485,7 +485,7 @@ class Home(http.Controller):
|
||||
:param unique: this parameters is not used, but mandatory: it is used by the HTTP stack to make a unique request
|
||||
:return: the menus (including the images in Base64)
|
||||
"""
|
||||
menus = request.env["ir.ui.menu"].load_menus(request.debug)
|
||||
menus = request.env["ir.ui.menu"].load_menus(request.session.debug)
|
||||
body = json.dumps(menus, default=ustr)
|
||||
response = request.make_response(body, [
|
||||
# this method must specify a content-type application/json instead of using the default text/html set because
|
||||
@@ -542,12 +542,6 @@ class Home(http.Controller):
|
||||
if not odoo.tools.config['list_db']:
|
||||
values['disable_database_manager'] = True
|
||||
|
||||
# otherwise no real way to test debug mode in template as ?debug =>
|
||||
# values['debug'] = '' but that's also the fallback value when
|
||||
# missing variables in qweb
|
||||
if 'debug' in values:
|
||||
values['debug'] = True
|
||||
|
||||
response = request.render('web.login', values)
|
||||
response.headers['X-Frame-Options'] = 'DENY'
|
||||
return response
|
||||
@@ -1590,8 +1584,7 @@ class Apps(http.Controller):
|
||||
action['views'] = [(False, u'form')]
|
||||
|
||||
sakey = Session().save_session_action(action)
|
||||
debug = '?debug' if req.debug else ''
|
||||
return werkzeug.utils.redirect('/web{0}#sa={1}'.format(debug, sakey))
|
||||
return werkzeug.utils.redirect('/web#sa={0}'.format(sakey))
|
||||
|
||||
|
||||
class ReportController(http.Controller):
|
||||
|
||||
@@ -17,7 +17,7 @@ class Http(models.AbstractModel):
|
||||
|
||||
def webclient_rendering_context(self):
|
||||
return {
|
||||
'menu_data': request.env['ir.ui.menu'].load_menus(request.debug),
|
||||
'menu_data': request.env['ir.ui.menu'].load_menus(request.session.debug),
|
||||
'session_info': json.dumps(self.session_info()),
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class Http(models.AbstractModel):
|
||||
lang = user_context.get("lang")
|
||||
translations_per_module, _ = request.env['ir.translation'].get_translations_for_webclient(mods, lang)
|
||||
|
||||
menu_json_utf8 = json.dumps(request.env['ir.ui.menu'].load_menus(request.debug), default=ustr, sort_keys=True).encode()
|
||||
menu_json_utf8 = json.dumps(request.env['ir.ui.menu'].load_menus(request.session.debug), default=ustr, sort_keys=True).encode()
|
||||
translations_json_utf8 = json.dumps(translations_per_module, sort_keys=True).encode()
|
||||
|
||||
return {
|
||||
|
||||
@@ -387,6 +387,14 @@
|
||||
<xpath expr="//t[@t-call='web._assets_frontend_minimal_js']" position="replace"/>
|
||||
</template>
|
||||
|
||||
<!-- Do not direcly call this template, call 'conditional_assets_tests' instead -->
|
||||
<template id="assets_tests" name="Tests Assets">
|
||||
</template>
|
||||
|
||||
<template id="conditional_assets_tests" name="Tests Assets Bundle">
|
||||
<t t-call-assets="web.assets_tests" t-if="'tests' in debug" defer_load="True" />
|
||||
</template>
|
||||
|
||||
<template id="web.layout" name="Web layout"><!DOCTYPE html>
|
||||
<html t-att="html_data or {}">
|
||||
<head>
|
||||
@@ -400,6 +408,7 @@
|
||||
<script type="text/javascript">
|
||||
var odoo = {
|
||||
csrf_token: "<t t-esc="request.csrf_token(None)"/>",
|
||||
debug: "<t t-esc="debug"/>",
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -422,6 +431,7 @@
|
||||
</script>
|
||||
<t t-call-assets="web.assets_common_minimal_js" t-css="false" defer_load="True"/>
|
||||
<t t-call-assets="web.assets_frontend_minimal_js" t-css="false" defer_load="True"/>
|
||||
<t t-call="web.conditional_assets_tests"/>
|
||||
<t t-call-assets="web.assets_common_lazy" t-css="false" lazy_load="True"/>
|
||||
<t t-call-assets="web.assets_frontend_lazy" t-css="false" lazy_load="True"/>
|
||||
</xpath>
|
||||
@@ -840,6 +850,7 @@
|
||||
<t t-call-assets="web.assets_backend" t-js="false"/>
|
||||
<t t-call-assets="web.assets_common" t-css="false"/>
|
||||
<t t-call-assets="web.assets_backend" t-css="false"/>
|
||||
<t t-call="web.conditional_assets_tests"/>
|
||||
|
||||
<![CDATA[
|
||||
<!--[if lt IE 10]>
|
||||
|
||||
@@ -73,7 +73,7 @@ class WebSettingsDashboard(http.Controller):
|
||||
'share': {
|
||||
'server_version': release.version,
|
||||
'expiration_date': expiration_date,
|
||||
'debug': request.debug,
|
||||
'debug': request.session.debug,
|
||||
'demo_active': demo_active,
|
||||
},
|
||||
'company': {
|
||||
|
||||
@@ -123,13 +123,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<a t-if="debug != true" class="oe_activate_debug_mode float-right" href="?debug" >Activate the developer mode</a>
|
||||
<br t-if="debug != true"/>
|
||||
<a t-if="debug != 'assets'" class="oe_activate_debug_mode float-right" href="?debug=assets" >Activate the developer mode (with assets)</a>
|
||||
<br t-if="debug != 'assets'"/>
|
||||
<a t-if="debug != false" class="oe_activate_debug_mode float-right" href="/web" >Deactivate the developer mode</a>
|
||||
<br t-if="debug != false"/>
|
||||
<a t-if="(debug) and !(demo_active)" class="oe_activate_debug_mode float-right o_web_settings_dashboard_force_demo" href="#">Load demo data</a>
|
||||
<div class="text-right">
|
||||
<a t-if="debug.indexOf('1') == -1" class="d-block" href="?debug=1">Activate the developer mode</a>
|
||||
<a t-if="debug.indexOf('assets') == -1" class="d-block" href="?debug=assets">Activate the developer mode (with assets)</a>
|
||||
<a t-if="debug.indexOf('tests') == -1" class="d-block" href="?debug=assets,tests">Activate the developer mode (with tests assets)</a>
|
||||
<a t-if="debug" class="d-block" href="?debug=">Deactivate the developer mode</a>
|
||||
<a t-if="debug and !demo_active" class="o_web_settings_dashboard_force_demo" href="#">Load demo data</a>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class QWeb(models.AbstractModel):
|
||||
atts = super(QWeb, self)._post_processing_att(tagName, atts, options)
|
||||
|
||||
if options.get('inherit_branding') or options.get('rendering_bundle') or \
|
||||
options.get('edit_translations') or options.get('debug') or (request and request.debug):
|
||||
options.get('edit_translations') or options.get('debug') or (request and request.session.debug):
|
||||
return atts
|
||||
|
||||
website = request and getattr(request, 'website', None)
|
||||
|
||||
@@ -93,7 +93,10 @@ class MockRequest(object):
|
||||
if not routing:
|
||||
app.match = werkzeugRaiseNotFound
|
||||
self.request = MockObject(
|
||||
env=env, context=context or {}, db=None, debug=False,
|
||||
env=env, context=context or {}, db=None,
|
||||
session=MockObject(
|
||||
debug=False
|
||||
),
|
||||
website=website, httprequest=MockObject(
|
||||
path='/hello/',
|
||||
app=app
|
||||
|
||||
@@ -904,7 +904,7 @@
|
||||
<div class="oe_structure">
|
||||
<h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
|
||||
</div>
|
||||
<t t-if="editable or request.debug">
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -969,7 +969,7 @@
|
||||
<li><a href="/contactus">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<t t-if="editable or request.debug">
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -996,7 +996,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-if="request.debug">
|
||||
<t t-if="debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -1123,7 +1123,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-if="editable or request.debug">
|
||||
<t t-if="editable or debug">
|
||||
<t t-call="website.http_error_debug"/>
|
||||
</t>
|
||||
</main>
|
||||
|
||||
@@ -135,7 +135,7 @@ class AssetsBundle(object):
|
||||
:returns [(tagName, attributes, content)] if the tag is auto close
|
||||
"""
|
||||
response = []
|
||||
if debug == 'assets':
|
||||
if debug and 'assets' in debug:
|
||||
if css and self.stylesheets:
|
||||
is_css_preprocessed, old_attachments = self.is_css_preprocessed()
|
||||
if not is_css_preprocessed:
|
||||
|
||||
@@ -24,6 +24,9 @@ from odoo.tools import pycompat, consteq
|
||||
from odoo.tools.mimetypes import guess_mimetype
|
||||
from odoo.modules.module import get_resource_path, get_module_path
|
||||
|
||||
from odoo.http import ALLOWED_DEBUG_MODES
|
||||
from odoo.tools.misc import str2bool
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -125,6 +128,21 @@ class IrHttp(models.AbstractModel):
|
||||
raise AccessDenied()
|
||||
return auth_method
|
||||
|
||||
@classmethod
|
||||
def _handle_debug(cls):
|
||||
# Store URL debug mode (might be empty) into session
|
||||
if 'debug' in request.httprequest.args:
|
||||
debug_mode = []
|
||||
for debug in request.httprequest.args['debug'].split(','):
|
||||
if debug not in ALLOWED_DEBUG_MODES:
|
||||
debug = '1' if str2bool(debug, debug) else ''
|
||||
debug_mode.append(debug)
|
||||
debug_mode = ','.join(debug_mode)
|
||||
|
||||
# Write on session only when needed
|
||||
if debug_mode != request.session.debug:
|
||||
request.session.debug = debug_mode
|
||||
|
||||
@classmethod
|
||||
def _serve_attachment(cls):
|
||||
env = api.Environment(request.cr, SUPERUSER_ID, request.context)
|
||||
@@ -162,6 +180,9 @@ class IrHttp(models.AbstractModel):
|
||||
|
||||
@classmethod
|
||||
def _handle_exception(cls, exception):
|
||||
# in case of Exception, e.g. 404, we don't step into _dispatch
|
||||
cls._handle_debug()
|
||||
|
||||
# If handle_exception returns something different than None, it will be used as a response
|
||||
|
||||
# This is done first as the attachment path may
|
||||
@@ -182,6 +203,8 @@ class IrHttp(models.AbstractModel):
|
||||
|
||||
@classmethod
|
||||
def _dispatch(cls):
|
||||
cls._handle_debug()
|
||||
|
||||
# locate the controller method
|
||||
try:
|
||||
rule, arguments = cls._find_handler(return_rule=True)
|
||||
|
||||
@@ -122,7 +122,7 @@ class IrUiMenu(models.Model):
|
||||
the menu hierarchy of the current user.
|
||||
Uses a cache for speeding up the computation.
|
||||
"""
|
||||
visible_ids = self._visible_menu_ids(request.debug if request else False)
|
||||
visible_ids = self._visible_menu_ids(request.session.debug if request else False)
|
||||
return self.filtered(lambda menu: menu.id in visible_ids)
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -1356,7 +1356,7 @@ actual arch.
|
||||
res_company=self.env.company.sudo(),
|
||||
keep_query=keep_query,
|
||||
request=request, # might be unbound if we're not in an httprequest context
|
||||
debug=request.debug if request else False,
|
||||
debug=request.session.debug if request else '',
|
||||
json=json,
|
||||
quote_plus=werkzeug.url_quote_plus,
|
||||
time=time,
|
||||
|
||||
+25
-29
@@ -64,6 +64,21 @@ STATIC_CACHE = 60 * 60 * 24 * 7
|
||||
# To remove when corrected in Babel
|
||||
babel.core.LOCALE_ALIASES['nb'] = 'nb_NO'
|
||||
|
||||
""" Debug mode is stored in session and should always be a string.
|
||||
It can be activated with an URL query string `debug=<mode>` where
|
||||
mode is either:
|
||||
- 'tests' to load tests assets
|
||||
- 'assets' to load assets non minified
|
||||
- any other truthy value to enable simple debug mode (to show some
|
||||
technical feature, to show complete traceback in frontend error..)
|
||||
- any falsy value to disable debug mode
|
||||
|
||||
You can use any truthy/falsy value from `str2bool` (eg: 'on', 'f'..)
|
||||
Multiple debug modes can be activated simultaneously, separated with
|
||||
a comma (eg: 'tests, assets').
|
||||
"""
|
||||
ALLOWED_DEBUG_MODES = ['', '1', 'assets', 'tests']
|
||||
|
||||
#----------------------------------------------------------
|
||||
# RequestHandler
|
||||
#----------------------------------------------------------
|
||||
@@ -143,15 +158,10 @@ def dispatch_rpc(service_name, method, params):
|
||||
odoo.tools.debugger.post_mortem(odoo.tools.config, sys.exc_info())
|
||||
raise
|
||||
|
||||
def local_redirect(path, query=None, keep_hash=False, forward_debug=True, code=303):
|
||||
def local_redirect(path, query=None, keep_hash=False, code=303):
|
||||
url = path
|
||||
if not query:
|
||||
query = {}
|
||||
if request and request.debug:
|
||||
if forward_debug:
|
||||
query['debug'] = ''
|
||||
else:
|
||||
query['debug'] = None
|
||||
if query:
|
||||
url += '?' + werkzeug.url_encode(query)
|
||||
if keep_hash:
|
||||
@@ -345,22 +355,6 @@ class WebRequest(object):
|
||||
return checked_call(self.db, *args, **kwargs)
|
||||
return self.endpoint(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def debug(self):
|
||||
""" Indicates whether the current request is in "debug" mode
|
||||
"""
|
||||
debug = 'debug' in self.httprequest.args
|
||||
if debug and self.httprequest.args.get('debug') == 'assets':
|
||||
debug = 'assets'
|
||||
|
||||
# check if request from rpc in debug mode
|
||||
if not debug:
|
||||
debug = self.httprequest.environ.get('HTTP_X_DEBUG_MODE')
|
||||
|
||||
if not debug and self.httprequest.referrer:
|
||||
debug = 'debug' in urls.url_parse(self.httprequest.referrer).decode_query()
|
||||
return debug
|
||||
|
||||
@contextlib.contextmanager
|
||||
def registry_cr(self):
|
||||
warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
|
||||
@@ -763,7 +757,7 @@ class HttpRequest(WebRequest):
|
||||
if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
|
||||
headers = {
|
||||
'Access-Control-Max-Age': 60 * 60 * 24,
|
||||
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode'
|
||||
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
|
||||
}
|
||||
return Response(status=200, headers=headers)
|
||||
|
||||
@@ -1038,7 +1032,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
|
||||
def logout(self, keep_db=False):
|
||||
for k in list(self):
|
||||
if not (keep_db and k == 'db'):
|
||||
if not (keep_db and k == 'db') and k != 'debug':
|
||||
del self[k]
|
||||
self._default_values()
|
||||
self.rotate = True
|
||||
@@ -1049,6 +1043,9 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
self.setdefault("login", None)
|
||||
self.setdefault("session_token", None)
|
||||
self.setdefault("context", {})
|
||||
# Force tests debug mode when test mode enabled to load 'assets_tests'
|
||||
test_mode = odoo.tools.config['test_enable'] or odoo.tools.config['test_file']
|
||||
self.setdefault("debug", 'tests' if test_mode else '')
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
@@ -1242,13 +1239,12 @@ class Response(werkzeug.wrappers.Response):
|
||||
class DisableCacheMiddleware(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
def start_wrapped(status, headers):
|
||||
referer = environ.get('HTTP_REFERER', '')
|
||||
parsed = urls.url_parse(referer)
|
||||
debug = parsed.query.count('debug') >= 1
|
||||
|
||||
if debug:
|
||||
req = werkzeug.wrappers.Request(environ)
|
||||
root.setup_session(req)
|
||||
if req.session and req.session.debug:
|
||||
new_headers = [('Cache-Control', 'no-cache')]
|
||||
|
||||
for k, v in headers:
|
||||
|
||||
+2
-2
@@ -1237,7 +1237,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
for group_ext_id in not_has_groups:
|
||||
if group_ext_id == 'base.group_no_one':
|
||||
# check: the group_no_one is effective in debug mode only
|
||||
if user.has_group(group_ext_id) and request and request.debug:
|
||||
if user.has_group(group_ext_id) and request and request.session.debug:
|
||||
return False
|
||||
else:
|
||||
if user.has_group(group_ext_id):
|
||||
@@ -1246,7 +1246,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
for group_ext_id in has_groups:
|
||||
if group_ext_id == 'base.group_no_one':
|
||||
# check: the group_no_one is effective in debug mode only
|
||||
if user.has_group(group_ext_id) and request and request.debug:
|
||||
if user.has_group(group_ext_id) and request and request.session.debug:
|
||||
return True
|
||||
else:
|
||||
if user.has_group(group_ext_id):
|
||||
|
||||
Reference in New Issue
Block a user