[IMP] test_main_flows: adapt The Main Flow Tour for mobile

This PR adapts some existing steps or add new ones to execute
the same tour on mobile.

This tour has many steps and give a good idea to know if new tasks
broke mobile or not.
The goal is that even if the design isn't perfect, users aren't blocked
and are able to perform their usual tasks.

The main flow tour is still running on desktop but
we also run it a second time on mobile.
It's only available in enterprise, otherwise the test will be skipped.
'375x667' correspond to the Bootstrap XS size.
This value is already used for the mobile tests suite.

Also see https://github.com/odoo/enterprise/pull/9011

Task ID: 2149402

closes odoo/odoo#46880

Related: odoo/enterprise#9011
Signed-off-by: Adrien Dieudonné (adr) <adr@odoo.com>
This commit is contained in:
Odoo's Mergebot
2020-03-11 12:01:31 +01:00
committed by GitHub
27 changed files with 681 additions and 257 deletions
@@ -9,7 +9,7 @@ odoo.define('account.dashboard.setup.tour', function (require) {
tour.register('account_render_report', {
test: true,
url: '/web',
}, [tour.STEPS.SHOW_APPS_MENU_ITEM,
}, [tour.stepUtils.showAppsMenuItem(),
{
id: 'account_menu_click',
trigger: '.o_app[data-menu-xmlid="account.menu_finance"]',
@@ -8,7 +8,7 @@ odoo.define('account.tax.group.tour.tests', function (require) {
tour.register('account_tax_group', {
test: true,
url: "/web",
}, [tour.STEPS.SHOW_APPS_MENU_ITEM,
}, [tour.stepUtils.showAppsMenuItem(),
{
content: "Go to Invoicing",
trigger: '.o_app[data-menu-xmlid="account.menu_finance"]',
+3 -3
View File
@@ -8,7 +8,7 @@ var _t = core._t;
tour.register('crm_tour', {
url: "/web",
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="crm.crm_menu_root"]',
content: _t("Ready to boost your sales? Your <b>Pipeline</b> can be found here, under the <b>CRM</b> app."),
position: 'right',
@@ -71,7 +71,7 @@ tour.register('crm_tour', {
extra_trigger: '.o_lead_opportunity_form',
content: _t("Use the breadcrumbs to <b>go back to your sales pipeline</b>."),
position: "bottom"
}, tour.STEPS.TOGGLE_HOME_MENU, tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, tour.stepUtils.toggleHomeMenu(), tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="base.menu_administration"]',
content: _t("Configuration options are available in the Settings app."),
position: "bottom",
@@ -85,7 +85,7 @@ tour.register('crm_tour', {
trigger: "div#invite_users .o_user_emails",
content: _t("<b>Invite coworkers</b> via email.<br/><i>Enter one email per line.</i>"),
position: "right"
}, tour.STEPS.TOGGLE_HOME_MENU, tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, tour.stepUtils.toggleHomeMenu(), tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="crm.crm_menu_root"]',
content: _t("Good job! You completed the tour of the CRM app."),
position: 'right',
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
tour.register('event_configurator_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
edition: 'community'
}, {
@@ -8,7 +8,7 @@ var _t = core._t;
tour.register('hr_expense_tour' , {
url: "/web"
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="hr_expense.menu_hr_expense_root"]',
content: _t("Want to manage your expenses? It starts here."),
position: 'right',
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
var _t = core._t;
tour.register('hr_recruitment_tour', [tour.STEPS.SHOW_APPS_MENU_ITEM, {
tour.register('hr_recruitment_tour', [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="hr_recruitment.menu_hr_recruitment_root"]',
content: _t('Want to <b>start recruiting</b> like a pro? <i>Start here.</i>'),
position: 'right',
@@ -9,7 +9,7 @@ var _t = core._t;
tour.register('point_of_sale_tour', {
url: "/web",
rainbowMan: false,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="point_of_sale.menu_point_root"]',
content: _t("Ready to launch your <b>point of sale</b>?"),
width: 215,
@@ -8,7 +8,7 @@ var _t = core._t;
tour.register('project_tour', {
url: "/web",
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="project.menu_main_pm"]',
content: _t('Want a better way to <b>manage your projects</b>? <i>It starts here.</i>'),
position: 'right',
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
tour.register('purchase_matrix_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="purchase.menu_purchase_root"]',
}, {
trigger: ".o_list_button_add",
+1 -1
View File
@@ -8,7 +8,7 @@ var _t = core._t;
tour.register('sale_tour', {
url: "/web",
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
content: _t('Open Sales app to send your first quotation in a few clicks.'),
position: 'right',
@@ -8,7 +8,7 @@ var optionVariantImage;
tour.register('sale_product_configurator_advanced_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]', // Note: The module sale_management is mandatory
edition: 'community'
}, {
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
tour.register('sale_product_configurator_edition_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
edition: 'community'
}, {
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
tour.register('sale_product_configurator_optional_products_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
edition: 'community'
}, {
@@ -8,7 +8,7 @@ tour.register('sale_product_configurator_pricelist_tour', {
test: true,
},
[
tour.STEPS.SHOW_APPS_MENU_ITEM,
tour.stepUtils.showAppsMenuItem(),
{
content: "navigate to the sale app",
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
tour.register('sale_product_configurator_single_custom_attribute_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
edition: 'community'
}, {
@@ -9,7 +9,7 @@ var tour = require('web_tour.tour');
tour.register('sale_product_configurator_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
edition: 'community'
}, {
@@ -6,7 +6,7 @@ var tour = require('web_tour.tour');
tour.register('sale_matrix_tour', {
url: "/web",
test: true,
}, [tour.STEPS.SHOW_APPS_MENU_ITEM, {
}, [tour.stepUtils.showAppsMenuItem(), {
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
}, {
trigger: ".o_list_button_add",
+5
View File
@@ -135,6 +135,7 @@ var Tip = Widget.extend({
p = $location.css("position");
} while (
$location.hasClass('dropdown-menu') ||
$location.hasClass('o_notebook_headers') ||
(
(o === "visible" || o === "hidden") &&
p !== "fixed" &&
@@ -319,6 +320,10 @@ Tip.getConsumeEventType = function ($element) {
if ($element.hasClass("o_field_date_range")) {
return "apply.daterangepicker input";
}
if (config.device.isMobile &&
$element.closest('.o_field_widget').is('.o_field_many2one, .o_field_many2many')) {
return "click";
}
return "input";
} else if ($element.hasClass('ui-draggable-handle')) {
return "drag";
+5 -26
View File
@@ -2,9 +2,11 @@ odoo.define('web_tour.TourManager', function(require) {
"use strict";
var core = require('web.core');
var config = require('web.config');
var local_storage = require('web.local_storage');
var mixins = require('web.mixins');
var utils = require('web_tour.utils');
var TourStepUtils = require('web_tour.TourStepUtils');
var RainbowMan = require('web.RainbowMan');
var RunningTourActionHelper = require('web_tour.RunningTourActionHelper');
var ServicesMixin = require('web.ServicesMixin');
@@ -116,7 +118,8 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, {
return tour.wait_for.then((function () {
tour.current_step = parseInt(local_storage.getItem(get_step_key(name))) || 0;
tour.steps = _.filter(tour.steps, (function (step) {
return !step.edition || step.edition === this.edition;
return (!step.edition || step.edition === this.edition) &&
(step.mobile === undefined || step.mobile === config.device.isMobile);
}).bind(this));
if (tour_is_consumed || tour.current_step >= tour.steps.length) {
@@ -425,30 +428,6 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, {
}
}
},
/**
* Tour predefined steps
*/
STEPS: {
SHOW_APPS_MENU_ITEM: {
edition: 'community',
trigger: '.o_menu_apps a',
auto: true,
position: "bottom",
},
TOGGLE_HOME_MENU: {
edition: "enterprise",
trigger: ".o_main_navbar .o_menu_toggle",
content: _t('Click on the <i>Home icon</i> to navigate across apps.'),
position: "bottom",
},
WEBSITE_NEW_PAGE: {
trigger: "#new-content-menu > a",
auto: true,
position: "bottom",
},
},
stepUtils: new TourStepUtils(this)
});
});
@@ -7,9 +7,6 @@ var rpc = require('web.rpc');
var session = require('web.session');
var TourManager = require('web_tour.TourManager');
if (config.device.isMobile) {
return Promise.reject();
}
/**
* @namespace
* @property {Object} active_tooltips
@@ -0,0 +1,160 @@
odoo.define('web_tour.TourStepUtils', function (require) {
'use strict';
const core = require('web.core');
const _t = core._t;
return core.Class.extend({
_getHelpMessage: (functionName, ...args) => `Generated by function tour utils ${functionName}(${args.join(', ')})`,
addDebugHelp: helpMessage => step => {
if (typeof step.debugHelp === 'string') {
step.debugHelp = step.debugHelp + '\n' + helpMessage;
} else {
step.debugHelp = helpMessage;
}
return step;
},
editionEnterpriseModifier(step) {
step.edition = 'enterprise';
return step;
},
mobileModifier(step) {
step.mobile = true;
return step;
},
showAppsMenuItem() {
return {
edition: 'community',
trigger: '.o_menu_apps a',
auto: true,
position: 'bottom',
};
},
toggleHomeMenu() {
return {
edition: 'enterprise',
trigger: '.o_main_navbar .o_menu_toggle',
content: _t('Click on the <i>Home icon</i> to navigate across apps.'),
position: 'bottom',
};
},
autoExpandMoreButtons(extra_trigger) {
return {
trigger: '.oe_button_box',
extra_trigger: extra_trigger,
auto: true,
run: actions => {
const $more = $('.oe_button_box .o_button_more');
if ($more.length) {
actions.click($more);
}
},
};
},
goBackBreadcrumbsMobile(description, ...extraTrigger) {
return extraTrigger.map(element => ({
mobile: true,
trigger: '.breadcrumb-item:not(.d-none):first',
extra_trigger: element,
content: description,
position: 'bottom',
debugHelp: this._getHelpMessage('goBackBreadcrumbsMobile', description, ...extraTrigger),
}));
},
goToAppSteps(dataMenuXmlid, description) {
return [
this.showAppsMenuItem(),
{
trigger: `.o_app[data-menu-xmlid="${dataMenuXmlid}"]`,
content: description,
position: 'right',
edition: 'community',
},
{
trigger: `.o_app[data-menu-xmlid="${dataMenuXmlid}"]`,
content: description,
position: 'bottom',
edition: 'enterprise',
},
].map(this.addDebugHelp(this._getHelpMessage('goToApp', dataMenuXmlid, description)));
},
openBuggerMenu(extraTrigger) {
return {
mobile: true,
trigger: '.o_mobile_menu_toggle',
extra_trigger: extraTrigger,
content: _t('Open bugger menu.'),
position: 'bottom',
debugHelp: this._getHelpMessage('openBuggerMenu', extraTrigger),
};
},
statusbarButtonsSteps(innerTextButton, description, extraTrigger) {
return [
{
mobile: true,
trigger: '.o_statusbar_buttons .btn.dropdown-toggle:contains(Action)',
extra_trigger: extraTrigger,
content: _t('Open Action Dropdown Menu.'),
position: 'bottom',
}, {
trigger: `.o_statusbar_buttons button:enabled:contains('${innerTextButton}')`,
content: description,
position: 'bottom',
},
].map(this.addDebugHelp(this._getHelpMessage('statusbarButtonsSteps', innerTextButton, description, extraTrigger)));
},
simulateEnterKeyboardInSearchModal() {
return {
mobile: true,
trigger: '.o_searchview_input',
extra_trigger: '.modal:not(.o_inactive_modal) .dropdown-menu.o_searchview_autocomplete',
position: 'bottom',
run: action => {
const keyboardEventUp = new KeyboardEvent('keyup', {
bubbles: true,
cancelable: true,
key: 'Enter',
code: 'Enter',
which: 13,
keyCode: 13
});
action.tip_widget.$anchor[0].dispatchEvent(keyboardEventUp);
},
debugHelp: this._getHelpMessage('simulateEnterKeyboardInSearchModal'),
};
},
mobileKanbanSearchMany2X(modalTitle, valueSearched) {
return [
{
mobile: true,
trigger: '.o_enable_searchview',
extra_trigger: `.modal:not(.o_inactive_modal) .modal-title:contains('${modalTitle}')`,
position: 'bottom',
}, {
mobile: true,
trigger: '.o_searchview_input',
position: 'bottom',
run: `text ${valueSearched}`,
},
this.simulateEnterKeyboardInSearchModal(),
{
mobile: true,
trigger: `.o_kanban_record .o_kanban_record_title :contains('${valueSearched}')`,
position: 'bottom',
},
].map(this.addDebugHelp(this._getHelpMessage('mobileKanbanSearchMany2X', modalTitle, valueSearched)));
},
});
});
+1
View File
@@ -10,6 +10,7 @@
<script type="text/javascript" src="/web_tour/static/src/js/running_tour_action_helper.js"></script>
<script type="text/javascript" src="/web_tour/static/src/js/tour_manager.js"></script>
<script type="text/javascript" src="/web_tour/static/src/js/tour_service.js"></script>
<script type="text/javascript" src="/web_tour/static/src/js/tour_step_utils.js"></script>
</xpath>
</template>
@@ -6,7 +6,7 @@ var tour = require("web_tour.tour");
tour.register("backend_dashboard", {
test: true,
url: "/web",
}, [tour.STEPS.SHOW_APPS_MENU_ITEM,
}, [tour.stepUtils.showAppsMenuItem(),
{
trigger: 'a[data-menu-xmlid="website.menu_website_configuration"]',
}, {
@@ -8,7 +8,11 @@ odoo.define("website_blog.tour", function (require) {
tour.register("blog", {
url: "/",
}, [tour.STEPS.WEBSITE_NEW_PAGE, {
}, [{
trigger: '#new-content-menu > a',
auto: true,
position: 'bottom',
}, {
trigger: "a[data-action=new_blog_post]",
content: _t("Select this menu item to create a new blog post."),
position: "bottom",
@@ -8,7 +8,11 @@ odoo.define("website_event.tour", function (require) {
tour.register("event", {
url: "/",
}, [tour.STEPS.WEBSITE_NEW_PAGE, {
}, [{
trigger: '#new-content-menu > a',
auto: true,
position: 'bottom',
}, {
trigger: "a[data-action=new_event]",
content: _t("Click here to create a new event."),
position: "bottom",
File diff suppressed because it is too large Load Diff
+22 -4
View File
@@ -1,12 +1,12 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import odoo
import odoo.tests
import unittest
class BaseTestUi(odoo.tests.HttpCase):
@odoo.tests.tagged('post_install', '-at_install')
class TestUi(odoo.tests.HttpCase):
def test_01_main_flow_tour(self):
def main_flow_tour(self):
# Define minimal accounting data to run without CoA
a_expense = self.env['account.account'].create({
'code': 'X2120',
@@ -68,3 +68,21 @@ class TestUi(odoo.tests.HttpCase):
})
self.start_tour("/web", 'main_flow_tour', login="admin", timeout=180)
@odoo.tests.tagged('post_install', '-at_install')
class TestUi(BaseTestUi):
def test_01_main_flow_tour(self):
self.main_flow_tour()
@odoo.tests.tagged('post_install', '-at_install')
class TestUiMobile(BaseTestUi):
browser_size = '375x667'
def test_01_main_flow_tour_mobile(self):
if odoo.release.version_info[-1] == 'e':
self.main_flow_tour()
else:
raise unittest.SkipTest("Mobile testing not needed in community")