From d7cd97a9be63ae0a8643400b56d6f9a03285fba6 Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Mon, 3 Jun 2019 14:26:02 +0000 Subject: [PATCH] [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 --- addons/hw_proxy/controllers/main.py | 2 +- addons/point_of_sale/controllers/main.py | 2 +- addons/point_of_sale/views/pos_templates.xml | 2 + addons/stock/controllers/main.py | 2 +- addons/stock/static/tests/route_tour.js | 2 +- addons/web/controllers/main.py | 11 +--- addons/web/models/ir_http.py | 4 +- addons/web/views/webclient_templates.xml | 11 ++++ .../controllers/main.py | 2 +- .../static/src/xml/dashboard.xml | 14 ++--- addons/website/models/ir_qweb.py | 2 +- addons/website/tests/test_qweb.py | 5 +- addons/website/views/website_templates.xml | 8 +-- odoo/addons/base/models/assetsbundle.py | 2 +- odoo/addons/base/models/ir_http.py | 23 ++++++++ odoo/addons/base/models/ir_ui_menu.py | 2 +- odoo/addons/base/models/ir_ui_view.py | 2 +- odoo/http.py | 54 +++++++++---------- odoo/models.py | 4 +- 19 files changed, 91 insertions(+), 63 deletions(-) diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py index a6c93e2939d..33e20f8a76a 100644 --- a/addons/hw_proxy/controllers/main.py +++ b/addons/hw_proxy/controllers/main.py @@ -101,7 +101,7 @@ class Proxy(http.Controller):

The list of connected USB devices as seen by the IoTBox

""" if debug is None: - resp += """(debug version)""" + resp += """(debug version)""" devices = subprocess.check_output("lsusb").decode('utf-8').split('\n') count = 0 resp += "
\n" diff --git a/addons/point_of_sale/controllers/main.py b/addons/point_of_sale/controllers/main.py index 79ec844fdf8..0fca1369384 100644 --- a/addons/point_of_sale/controllers/main.py +++ b/addons/point_of_sale/controllers/main.py @@ -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'), diff --git a/addons/point_of_sale/views/pos_templates.xml b/addons/point_of_sale/views/pos_templates.xml index dfadf1af349..cb1a8a81a01 100644 --- a/addons/point_of_sale/views/pos_templates.xml +++ b/addons/point_of_sale/views/pos_templates.xml @@ -31,12 +31,14 @@ var odoo = { csrf_token: "", session_info: , + debug: "", }; + @@ -422,6 +431,7 @@ + @@ -840,6 +850,7 @@ + diff --git a/addons/web_settings_dashboard/controllers/main.py b/addons/web_settings_dashboard/controllers/main.py index 574f9e8fd21..fb1105ba939 100644 --- a/addons/web_settings_dashboard/controllers/main.py +++ b/addons/web_settings_dashboard/controllers/main.py @@ -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': { diff --git a/addons/web_settings_dashboard/static/src/xml/dashboard.xml b/addons/web_settings_dashboard/static/src/xml/dashboard.xml index b8c8f339ebd..5e55e8d7a87 100644 --- a/addons/web_settings_dashboard/static/src/xml/dashboard.xml +++ b/addons/web_settings_dashboard/static/src/xml/dashboard.xml @@ -123,13 +123,13 @@

- Activate the developer mode -
- Activate the developer mode (with assets) -
- Deactivate the developer mode -
- Load demo data +
+ Activate the developer mode + Activate the developer mode (with assets) + Activate the developer mode (with tests assets) + Deactivate the developer mode + Load demo data +
diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index ea33aa114b2..664838afe77 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -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) diff --git a/addons/website/tests/test_qweb.py b/addons/website/tests/test_qweb.py index e3070d9f009..5ca9344aa32 100644 --- a/addons/website/tests/test_qweb.py +++ b/addons/website/tests/test_qweb.py @@ -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 diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index c6f112d554d..3c5904dd034 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -904,7 +904,7 @@

:

- + @@ -969,7 +969,7 @@
  • Contact Us
  • - + @@ -996,7 +996,7 @@ - + @@ -1123,7 +1123,7 @@ - + diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py index faf8a3cb8fd..04e85944c0e 100644 --- a/odoo/addons/base/models/assetsbundle.py +++ b/odoo/addons/base/models/assetsbundle.py @@ -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: diff --git a/odoo/addons/base/models/ir_http.py b/odoo/addons/base/models/ir_http.py index 420ba00026f..ac6ccbc9eac 100644 --- a/odoo/addons/base/models/ir_http.py +++ b/odoo/addons/base/models/ir_http.py @@ -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) diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py index 7459dd29504..d726ee2cfa1 100644 --- a/odoo/addons/base/models/ir_ui_menu.py +++ b/odoo/addons/base/models/ir_ui_menu.py @@ -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 diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py index 8c5f4809f32..30600ee6bf7 100644 --- a/odoo/addons/base/models/ir_ui_view.py +++ b/odoo/addons/base/models/ir_ui_view.py @@ -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, diff --git a/odoo/http.py b/odoo/http.py index 67243cf5a52..3f4dd6fad27 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -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=` 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: diff --git a/odoo/models.py b/odoo/models.py index a2499c6a68d..77701cf676a 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -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):