diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index b16c0c0c2cb9b54384c526beb40f30e77588e675..8232937897555f2f1a8750bff8959642f9bf7f6a 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 a214d8b55a4b364091b6875d77ce27cf849ce930..8007dc2ab7ea0bb78e01c81fcf9cdb5817203ad8 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 12f979a3c370f30919355c29f1f2d4d25fb28369..79baca71589143e5c25d6c74229456794fe3c5ee 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 0000000000000000000000000000000000000000..d777f5bd87b3e3ba60603a62ddc03a96f4e1493e --- /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 cfd9e1210ba9e82e2134f029ab77f9d7c010c1a2..f614fa9af64ba8462ee75e3977a292317ffdcbfc 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 17eb5c8beb2f4aa28a659c04ec468f162f725fd9..0b1f8ed3763c7068a8285e4448d083431a293d47 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 77a5c4bedba77b77e951fb8d41fc0e1ef711a5f4..5117d1e633c8ccc5e6eff831074871100afa37f5 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 8b7b51944a592b8882db586744e7261c2217b7b3..4bfe84887d02abcea4b931066f8cd1da56fe0e57 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>