From 9bc5009481de188b72da21bd7a0d246a60a070ef Mon Sep 17 00:00:00 2001
From: Aaron Bohy <aab@odoo.com>
Date: Fri, 27 Oct 2017 11:32:10 +0200
Subject: [PATCH] [FIX] web(_editor): enable mobile tests

This rev. introduces a new test suite meant to test the webclient
components on mobile devices. The key 'config.device.isMobile' is
forced to true in this test suite, so that mobile specific JS files
are properly executed, which isn't the case in the classic JS test
suite (setting isMobile to true in the test definition is too late,
as the JS files are already processed).

For now, this new test suite contains a single test, which was
skipped until this rev. as it couldn't be executed in the classical
JS test suite.

Both suites are executed at each build of the runbot, and they
can be manually executed from the webclient as well (via the debug
manager).
---
 addons/web/controllers/main.py                |   4 +
 .../static/src/js/widgets/debug_manager.js    |  15 +-
 addons/web/static/src/xml/base.xml            |   1 +
 .../static/tests/views/kanban_mobile_tests.js | 101 ++++++++++++
 addons/web/static/tests/views/kanban_tests.js |  49 ------
 addons/web/tests/test_js.py                   |   5 +
 addons/web/views/webclient_templates.xml      | 144 ++++++++++++------
 addons/web_editor/views/editor.xml            |   8 +-
 8 files changed, 225 insertions(+), 102 deletions(-)
 create mode 100644 addons/web/static/tests/views/kanban_mobile_tests.js

diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index b16c0c0c2cb9..823293789755 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -612,6 +612,10 @@ class WebClient(http.Controller):
     def test_suite(self, mod=None, **kwargs):
         return request.render('web.qunit_suite')
 
+    @http.route('/web/tests/mobile', type='http', auth="none")
+    def test_mobile_suite(self, mod=None, **kwargs):
+        return request.render('web.qunit_mobile_suite')
+
     @http.route('/web/benchmarks', type='http', auth="none")
     def benchmarks(self, mod=None, **kwargs):
         return request.render('web.benchmark_suite')
diff --git a/addons/web/static/src/js/widgets/debug_manager.js b/addons/web/static/src/js/widgets/debug_manager.js
index a214d8b55a4b..8007dc2ab7ea 100644
--- a/addons/web/static/src/js/widgets/debug_manager.js
+++ b/addons/web/static/src/js/widgets/debug_manager.js
@@ -6,7 +6,6 @@ var dialogs = require('web.view_dialogs');
 var core = require('web.core');
 var Dialog = require('web.Dialog');
 var field_utils = require('web.field_utils');
-var framework = require('web.framework');
 var session = require('web.session');
 var SystrayMenu = require('web.SystrayMenu');
 var utils = require('web.utils');
@@ -189,6 +188,9 @@ var DebugManager = Widget.extend({
             }
         }).open();
     },
+    /**
+     * Runs the JS (desktop) tests
+     */
     perform_js_tests: function () {
         this.do_action({
             name: _t("JS Tests"),
@@ -197,6 +199,17 @@ var DebugManager = Widget.extend({
             url: '/web/tests?mod=*'
         });
     },
+    /**
+     * Runs the JS mobile tests
+     */
+    perform_js_mobile_tests: function () {
+        this.do_action({
+            name: _t("JS Mobile Tests"),
+            target: 'new',
+            type: 'ir.actions.act_url',
+            url: '/web/tests/mobile?mod=*'
+        });
+    },
     split_assets: function() {
         window.location = $.param.querystring(window.location.href, 'debug=assets');
     },
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 12f979a3c370..79baca715891 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -220,6 +220,7 @@
 </t>
 <t t-name="WebClient.DebugManager.Global">
     <li><a href="#" data-action="perform_js_tests">Run JS Tests</a></li>
+    <li><a href="#" data-action="perform_js_mobile_tests">Run JS Mobile Tests</a></li>
     <li><a href="#" data-action="select_view">Open View</a></li>
     <t t-if="manager._events">
         <li class="divider"/>
diff --git a/addons/web/static/tests/views/kanban_mobile_tests.js b/addons/web/static/tests/views/kanban_mobile_tests.js
new file mode 100644
index 000000000000..d777f5bd87b3
--- /dev/null
+++ b/addons/web/static/tests/views/kanban_mobile_tests.js
@@ -0,0 +1,101 @@
+odoo.define('web.kanban_mobile_tests', function (require) {
+"use strict";
+
+var KanbanView = require('web.KanbanView');
+var testUtils = require('web.test_utils');
+
+var createView = testUtils.createView;
+
+QUnit.module('Views', {
+    beforeEach: function () {
+        this.data = {
+            partner: {
+                fields: {
+                    foo: {string: "Foo", type: "char"},
+                    bar: {string: "Bar", type: "boolean"},
+                    int_field: {string: "int_field", type: "integer", sortable: true},
+                    qux: {string: "my float", type: "float"},
+                    product_id: {string: "something_id", type: "many2one", relation: "product"},
+                    category_ids: { string: "categories", type: "many2many", relation: 'category'},
+                    state: { string: "State", type: "selection", selection: [["abc", "ABC"], ["def", "DEF"], ["ghi", "GHI"]]},
+                    date: {string: "Date Field", type: 'date'},
+                    datetime: {string: "Datetime Field", type: 'datetime'},
+                },
+                records: [
+                    {id: 1, bar: true, foo: "yop", int_field: 10, qux: 0.4, product_id: 3, state: "abc", category_ids: []},
+                    {id: 2, bar: true, foo: "blip", int_field: 9, qux: 13, product_id: 5, state: "def", category_ids: [6]},
+                    {id: 3, bar: true, foo: "gnap", int_field: 17, qux: -3, product_id: 3, state: "ghi", category_ids: [7]},
+                    {id: 4, bar: false, foo: "blip", int_field: -4, qux: 9, product_id: 5, state: "ghi", category_ids: []},
+                ]
+            },
+            product: {
+                fields: {
+                    id: {string: "ID", type: "integer"},
+                    name: {string: "Display Name", type: "char"},
+                },
+                records: [
+                    {id: 3, name: "hello"},
+                    {id: 5, name: "xmo"},
+                ]
+            },
+            category: {
+                fields: {
+                    name: {string: "Category Name", type: "char"},
+                    color: {string: "Color index", type: "integer"},
+                },
+                records: [
+                    {id: 6, name: "gold", color: 2},
+                    {id: 7, name: "silver", color: 5},
+                ]
+            },
+        };
+    },
+}, function () {
+
+    QUnit.module('KanbanView Mobile');
+
+    QUnit.test('mobile grouped rendering', function (assert) {
+        assert.expect(8);
+
+        var kanban = createView({
+            View: KanbanView,
+            model: 'partner',
+            data: this.data,
+            arch: '<kanban class="o_kanban_test o_kanban_small_column" on_create="quick_create">' +
+                    '<templates><t t-name="kanban-box">' +
+                        '<div><field name="foo"/></div>' +
+                    '</t></templates>' +
+                '</kanban>',
+            groupBy: ['product_id'],
+        });
+
+        // basic rendering tests
+        assert.strictEqual(kanban.$('.o_kanban_group').length, 2, "should have 2 columns" );
+        assert.ok(kanban.$('.o_kanban_mobile_tab:first').hasClass('o_current'),
+            "first tab is the active tab with class 'o_current'");
+        assert.strictEqual(kanban.$('.o_kanban_group:first > div.o_kanban_record').length, 2,
+            "there are 2 records in active tab");
+        assert.strictEqual(kanban.$('.o_kanban_group:nth(1) > div.o_kanban_record').length, 0,
+            "there is no records in next tab. Records will be loaded when it will be opened");
+
+        // quick create in first column
+        kanban.$buttons.find('.o-kanban-button-new').click();
+        assert.ok(kanban.$('.o_kanban_group:nth(0) > div:nth(1)').hasClass('o_kanban_quick_create'),
+            "clicking on create should open the quick_create in the first column");
+
+        // move to second column
+        kanban.$('.o_kanban_mobile_tab:nth(1)').trigger('click');
+        assert.ok(kanban.$('.o_kanban_mobile_tab:nth(1)').hasClass('o_current'),
+            "second tab is now active with class 'o_current'");
+        assert.strictEqual(kanban.$('.o_kanban_group:nth(1) > div.o_kanban_record').length, 2,
+            "the 2 records of the second group have now been loaded");
+
+        // quick create in second column
+        kanban.$buttons.find('.o-kanban-button-new').click();
+        assert.ok(kanban.$('.o_kanban_group:nth(1) >  div:nth(1)').hasClass('o_kanban_quick_create'),
+            "clicking on create should open the quick_create in the second column");
+
+        kanban.destroy();
+    });
+});
+});
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index cfd9e1210ba9..f614fa9af64b 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -2129,55 +2129,6 @@ QUnit.module('Views', {
                 "quick create should have been added in the first column");
         }
     });
-
-    QUnit.skip('mobile grouped rendering', function (assert) {
-        // Temporarily disable this test until we introduce a mobile test suite, as
-        // the code of the kanban renderer for mobile is in a specific file which isn't
-        // executed in desktop (so setting 'isMobile: True' in the test is useless).
-        // So to re-activate this test, it will need to be moved in another file
-        // included in the bundle of the mobile test suite.
-        assert.expect(8);
-        var done = assert.async();
-
-        createAsyncView({
-            View: KanbanView,
-            model: 'partner',
-            data: this.data,
-            arch: '<kanban class="o_kanban_test o_kanban_small_column" on_create="quick_create">' +
-                    '<templates><t t-name="kanban-box">' +
-                        '<div><field name="foo"/></div>' +
-                    '</t></templates>' +
-                '</kanban>',
-            groupBy: ['product_id'],
-            config: {device: {isMobile: true}},
-        }).then(function (kanban) {
-
-            // Dummy dom update trigger for activate mobile tabs and move to first column
-            core.bus.trigger("DOM_updated");
-
-            assert.equal(kanban.$el.find('.o_kanban_group').length, 2, "2 colomns are created" );
-
-            kanban.$buttons.find('.o-kanban-button-new').click(); // Click on 'Create'
-            assert.ok(kanban.$('.o_kanban_group:nth(0) > div:nth(1)').hasClass('o_kanban_quick_create'),
-                "clicking on create should open the quick_create in the first column");
-
-            assert.equal(kanban.$el.find('.o_kanban_mobile_tab.current > span').html(), "hello", "First tab 'hello' is active tab with class 'current'" );
-            assert.equal(kanban.$el.find('.o_kanban_group.current > div.o_kanban_record').length, "2", "there is 2 record in active 'hello' tab" );
-            assert.equal(kanban.$el.find('.o_kanban_group.next > div.o_kanban_record').length, "0", "there is 0 record in next tab. Records will load when click on next tab");
-
-            kanban.$el.find('.o_kanban_mobile_tab.next').trigger('click'); // Moving to next tab
-            assert.equal(kanban.$el.find('.o_kanban_mobile_tab.current > span').html(), "xmo", "Second tab 'xmo' is active with class 'current'" );
-            assert.equal(kanban.$el.find('.o_kanban_group.current > div.o_kanban_record').length, "2", "there is 2 record in active 'xmo' tab. Records are loaded after click on tab");
-
-            kanban.$buttons.find('.o-kanban-button-new').click(); // Click on 'Create'
-            assert.ok(kanban.$('.o_kanban_group:nth(1) >  div:nth(1)').hasClass('o_kanban_quick_create'),
-                "clicking on create should open the quick_create in the second column");
-
-            kanban.destroy();
-            done();
-        });
-    });
-
 });
 
 });
diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py
index 17eb5c8beb2f..0b1f8ed3763c 100644
--- a/addons/web/tests/test_js.py
+++ b/addons/web/tests/test_js.py
@@ -11,8 +11,13 @@ class WebSuite(odoo.tests.HttpCase):
     at_install = False
 
     def test_01_js(self):
+        # webclient desktop test suite
         self.phantom_js('/web/tests?mod=web', "", "", login='admin', timeout=300)
 
+    def test_02_js(self):
+        # webclient mobile test suite
+        self.phantom_js('/web/tests/mobile?mod=web', "", "", login='admin', timeout=300)
+
     def test_check_suite(self):
         # verify no js test is using `QUnit.only` as it forbid any other test to be executed
         re_only = re.compile('QUnit\.only\(')
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 77a5c4bedba7..5117d1e633c8 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -396,60 +396,67 @@
         </a>
     </template>
 
+    <template id="web.js_tests_assets">
+        <link type="text/css" rel="stylesheet" href="/web/static/lib/qunit/qunit-2.2.1.css"/>
+        <script type="text/javascript" src="/web/static/lib/qunit/qunit-2.2.1.js"></script>
+        <script type="text/javascript" src="/web/static/tests/helpers/qunit_config.js"></script>
+
+        <t t-call-assets="web.assets_common" t-js="false"/>
+        <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"/>
+
+        <!-- add lazy-loaded libs to make tests synchronous -->
+        <link rel="stylesheet" href="/web/static/lib/fullcalendar/css/fullcalendar.css"/>
+        <script type="text/javascript" src="/web/static/lib/fullcalendar/js/fullcalendar.js"></script>
+        <link rel="stylesheet" type="text/css" href="/web/static/lib/nvd3/nv.d3.css"/>
+        <script type="text/javascript" src="/web/static/lib/nvd3/d3.v3.js"></script>
+        <script type="text/javascript" src="/web/static/lib/nvd3/nv.d3.js"></script>
+        <script type="text/javascript" src="/web/static/src/js/libs/nvd3.js"></script>
+        <script type="text/javascript" src="/web/static/lib/ace/ace.odoo-custom.js"></script>
+        <script type="text/javascript" src="/web/static/lib/ace/mode-python.js"></script>
+        <script type="text/javascript" src="/web/static/lib/ace/mode-xml.js"></script>
+
+        <script type="text/javascript">
+            // define the 'web.web_client' module because some other modules require it
+            odoo.define('web.web_client', function (require) {
+                var WebClient = require('web.WebClient');
+                var web_client = new WebClient();
+                // override _call_service to prevent the web_client from doing RPCs
+                web_client._call_service = function () {};
+                return web_client;
+            });
+        </script>
+
+        <style>
+            body {
+                position: relative; // bootstrap-datepicker needs this
+            }
+            body:not(.debug) .modal-backdrop, body:not(.debug) .modal, body:not(.debug) .ui-autocomplete {
+                opacity: 0 !important;
+            }
+            #qunit-testrunner-toolbar label {
+                font-weight: inherit;
+                margin-bottom: inherit;
+            }
+            #qunit-testrunner-toolbar input[type=text] {
+                width: inherit;
+                display: inherit;
+            }
+        </style>
+
+        <script type="text/javascript" src="/web/static/tests/helpers/test_utils.js"></script>
+        <script type="text/javascript" src="/web/static/tests/helpers/mock_server.js"></script>
+
+        <script type="text/javascript" src="/web/static/tests/boot_tests.js"></script>
+    </template>
+
     <template id="web.qunit_suite">
         <t t-call="web.layout">
             <t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
             <t t-set="title">Web Tests</t>
             <t t-set="head">
-                <link type="text/css" rel="stylesheet" href="/web/static/lib/qunit/qunit-2.2.1.css"/>
-                <script type="text/javascript" src="/web/static/lib/qunit/qunit-2.2.1.js"></script>
-                <script type="text/javascript" src="/web/static/tests/helpers/qunit_config.js"></script>
-
-                <t t-call-assets="web.assets_common" t-js="false"/>
-                <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"/>
-
-                <!-- add lazy-loaded libs -->
-                <link rel="stylesheet" href="/web/static/lib/fullcalendar/css/fullcalendar.css"/>
-                <script type="text/javascript" src="/web/static/lib/fullcalendar/js/fullcalendar.js"></script>
-                <link rel="stylesheet" type="text/css" href="/web/static/lib/nvd3/nv.d3.css"/>
-                <script type="text/javascript" src="/web/static/lib/nvd3/d3.v3.js"></script>
-                <script type="text/javascript" src="/web/static/lib/nvd3/nv.d3.js"></script>
-                <script type="text/javascript" src="/web/static/src/js/libs/nvd3.js"></script>
-                <script type="text/javascript" src="/web/static/lib/ace/ace.odoo-custom.js"></script>
-                <script type="text/javascript" src="/web/static/lib/ace/mode-python.js"></script>
-                <script type="text/javascript" src="/web/static/lib/ace/mode-xml.js"></script>
-
-                <script type="text/javascript">
-                    // define the 'web.web_client' module because some other modules require it
-                    odoo.define('web.web_client', function (require) {
-                        var WebClient = require('web.WebClient');
-                        var web_client = new WebClient();
-                        // override _call_service to prevent the web_client from doing RPCs
-                        web_client._call_service = function () {};
-                        return web_client;
-                    });
-                </script>
-
-                <style>
-                    body {
-                        position: relative; // bootstrap-datepicker needs this
-                    }
-                    body:not(.debug) .modal-backdrop, body:not(.debug) .modal, body:not(.debug) .ui-autocomplete {
-                        opacity: 0 !important;
-                    }
-                    #qunit-testrunner-toolbar label {
-                        font-weight: inherit;
-                        margin-bottom: inherit;
-                    }
-                    #qunit-testrunner-toolbar input[type=text] {
-                        width: inherit;
-                        display: inherit;
-                    }
-                </style>
-                <script type="text/javascript" src="/web/static/tests/helpers/test_utils.js"></script>
-                <script type="text/javascript" src="/web/static/tests/helpers/mock_server.js"></script>
+                <t t-call="web.js_tests_assets"/>
 
                 <script type="text/javascript" src="/web/static/tests/fields/basic_fields_tests.js"></script>
                 <script type="text/javascript" src="/web/static/tests/fields/field_utils_tests.js"></script>
@@ -486,8 +493,45 @@
                 <script type="text/javascript" src="/web/static/tests/widgets/domain_selector_tests.js"/>
                 <script type="text/javascript" src="/web/static/tests/widgets/model_field_selector_tests.js"/>
                 <script type="text/javascript" src="/web/static/tests/widgets/rainbow_man_tests.js"/>
+            </t>
+
+            <div id="qunit"/>
+            <div id="qunit-fixture"/>
+        </t>
+    </template>
+
+    <template id="web.qunit_mobile_suite">
+        <t t-call="web.layout">
+            <t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
+            <t t-set="title">Web Mobile Tests</t>
+            <t t-set="head">
+                <script>
+                    // force the config.device.isMobile key to be true so that
+                    // mobile specific files aren't rejected
+                    window.odoo = {};
+                    var odooDefine;
+                    Object.defineProperty(window.odoo, 'define', {
+                        get: function () {
+                            return odooDefine;
+                        },
+                        set: function (define) {
+                            odooDefine = function () {
+                                define.apply(this, arguments);
+                                if (arguments[0] === 'web.config') {
+                                    define.call(this, 'web.config.patch', function (require) {
+                                        var config = require('web.config');
+                                        config.device.isMobile = true;
+                                    });
+                                }
+                            };
+                        },
+                    });
+                </script>
+
+                <t t-call="web.js_tests_assets"/>
+                <script type="text/javascript" src="/web/static/lib/jquery.touchSwipe/jquery.touchSwipe.js"></script>
 
-                <script type="text/javascript" src="/web/static/tests/boot_tests.js"></script>
+                <script type="text/javascript" src="/web/static/tests/views/kanban_mobile_tests.js"></script>
             </t>
 
             <div id="qunit"/>
diff --git a/addons/web_editor/views/editor.xml b/addons/web_editor/views/editor.xml
index 8b7b51944a59..4bfe84887d02 100644
--- a/addons/web_editor/views/editor.xml
+++ b/addons/web_editor/views/editor.xml
@@ -114,10 +114,14 @@
         <t t-call-assets="web_editor.assets_editor" t-css="false"/>
     </xpath>
 </template>
-<template id="qunit_suite" inherit_id="web.qunit_suite">
-    <xpath expr="//t[@t-call-assets='web.assets_backend'][@t-css='false']" position="after">
+<template id="js_tests_assets" inherit_id="web.js_tests_assets">
+    <xpath expr="." position="inside">
         <t t-call-assets="web_editor.summernote" t-css="false"/>
         <t t-call-assets="web_editor.assets_editor" t-css="false"/>
+    </xpath>
+</template>
+<template id="qunit_suite" inherit_id="web.qunit_suite">
+    <xpath expr="." position="inside">
         <script type="text/javascript" src="/web_editor/static/tests/web_editor_tests.js"></script>
     </xpath>
 </template>
-- 
GitLab