合并企业版代码(未测试,先提交到测试分支)
This commit is contained in:
12
web_enterprise/static/tests/helpers.js
Normal file
12
web_enterprise/static/tests/helpers.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { createWebClient } from "@web/../tests/webclient/helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { legacyServiceProvider } from "@web_enterprise/legacy/legacy_service_provider";
|
||||
import { WebClientEnterprise } from "@web_enterprise/webclient/webclient";
|
||||
|
||||
export function createEnterpriseWebClient(params) {
|
||||
params.WebClientClass = WebClientEnterprise;
|
||||
registry.category("services").add("enterprise_legacy_service_provider", legacyServiceProvider);
|
||||
return createWebClient(params);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { createWebClient, doAction, getActionManagerServerData, loadState } from "@web/../tests/webclient/helpers";
|
||||
import { click, getFixture, legacyExtraNextTick } from "@web/../tests/helpers/utils";
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
||||
QUnit.module('ActionManager', {
|
||||
beforeEach() {
|
||||
serverData = getActionManagerServerData();
|
||||
target = getFixture();
|
||||
Object.assign(serverData, {
|
||||
actions: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: 'Partners Action 1',
|
||||
res_model: 'partner',
|
||||
type: 'ir.actions.act_window',
|
||||
views: [[false, 'list'], [false, 'kanban'], [false, 'form']],
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
name: 'Partners Action 2',
|
||||
res_model: 'partner',
|
||||
type: 'ir.actions.act_window',
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
},
|
||||
},
|
||||
views: {
|
||||
'partner,false,kanban': `
|
||||
<kanban>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click">
|
||||
<field name="foo"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
'partner,false,list': '<tree><field name="foo"/></tree>',
|
||||
'partner,false,form':
|
||||
`<form>
|
||||
<group>
|
||||
<field name="display_name"/>
|
||||
</group>
|
||||
</form>`,
|
||||
'partner,false,search': '<search><field name="foo" string="Foo"/></search>',
|
||||
},
|
||||
models: {
|
||||
partner: {
|
||||
fields: {
|
||||
foo: { string: "Foo", type: "char" },
|
||||
},
|
||||
records: [
|
||||
{ id: 1, display_name: "First record", foo: "yop" },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('uses a mobile-friendly view by default (if possible)', async function (assert) {
|
||||
const webClient = await createWebClient({ serverData });
|
||||
// should default on a mobile-friendly view (kanban) for action 1
|
||||
await doAction(webClient, 1);
|
||||
|
||||
assert.containsNone(target, '.o_list_view');
|
||||
assert.containsOnce(target, '.o_kanban_view');
|
||||
|
||||
// there is no mobile-friendly view for action 2, should use the first one (list)
|
||||
await doAction(webClient, 2);
|
||||
|
||||
assert.containsOnce(target, '.o_list_view');
|
||||
assert.containsNone(target, '.o_kanban_view');
|
||||
});
|
||||
|
||||
QUnit.test('lazy load mobile-friendly view', async function (assert) {
|
||||
const mockRPC = (route, args) => {
|
||||
assert.step(args.method || route);
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
|
||||
await loadState(webClient, {
|
||||
action: 1,
|
||||
view_type: 'form',
|
||||
});
|
||||
|
||||
assert.containsNone(target, '.o_list_view');
|
||||
assert.containsNone(target, '.o_kanban_view');
|
||||
assert.containsOnce(target, '.o_form_view');
|
||||
|
||||
// go back to lazy loaded view
|
||||
await click(target, '.o_control_panel .breadcrumb .o_back_button');
|
||||
await legacyExtraNextTick();
|
||||
assert.containsNone(target, '.o_form_view');
|
||||
assert.containsNone(target, '.o_list_view');
|
||||
assert.containsOnce(target, '.o_kanban_view');
|
||||
|
||||
assert.verifySteps([
|
||||
'/web/webclient/load_menus',
|
||||
'/web/action/load',
|
||||
'get_views',
|
||||
'onchange', // default_get/onchange to open form view
|
||||
'web_search_read', // web search read when coming back to Kanban
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test('view switcher button should be displayed in dropdown on mobile screens', async function (assert) {
|
||||
// This test will spawn a kanban view (mobile friendly).
|
||||
// so, the "legacy" code won't be tested here.
|
||||
const webClient = await createWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, 1);
|
||||
|
||||
assert.containsOnce(target.querySelector('.o_control_panel'), '.o_cp_switch_buttons > .o-dropdown > button');
|
||||
assert.containsNone(target.querySelector('.o_control_panel'), '.o_cp_switch_buttons .o-dropdown--menu .o_switch_view.o_kanban');
|
||||
assert.containsNone(target.querySelector('.o_control_panel'), '.o_cp_switch_buttons .o-dropdown--menu button.o_switch_view');
|
||||
|
||||
assert.hasClass(target.querySelector('.o_control_panel .o_cp_switch_buttons > .o-dropdown > button > i'), 'oi-view-kanban');
|
||||
await click(target, '.o_control_panel .o_cp_switch_buttons > .o-dropdown > button');
|
||||
|
||||
assert.hasClass(target.querySelector('.o_cp_switch_buttons .o-dropdown--menu button.o_switch_view.o_kanban'), 'active');
|
||||
assert.doesNotHaveClass(target.querySelector('.o_cp_switch_buttons .o-dropdown--menu button.o_switch_view.o_list'), 'active');
|
||||
assert.hasClass(target.querySelector('.o_cp_switch_buttons .o-dropdown--menu button.o_switch_view.o_kanban'), 'oi-view-kanban');
|
||||
});
|
||||
160
web_enterprise/static/tests/legacy/barcodes_mobile_tests.js
Normal file
160
web_enterprise/static/tests/legacy/barcodes_mobile_tests.js
Normal file
@@ -0,0 +1,160 @@
|
||||
odoo.define('web_mobile.barcode.tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const fieldRegistry = require('web.field_registry');
|
||||
const FormView = require('web.FormView');
|
||||
const { FieldMany2One } = require('web.relational_fields');
|
||||
const { createView, dom, mock } = require('web.test_utils');
|
||||
|
||||
const FieldMany2OneBarcode = require('web_mobile.barcode_fields');
|
||||
const BarcodeScanner = require('@web/webclient/barcode/barcode_scanner');
|
||||
|
||||
const NAME_SEARCH = "name_search";
|
||||
const PRODUCT_PRODUCT = 'product.product';
|
||||
const SALE_ORDER_LINE = 'sale_order_line';
|
||||
const PRODUCT_FIELD_NAME = 'product_id';
|
||||
const ARCHS = {
|
||||
'product.product,false,kanban': `
|
||||
<kanban><templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click"><field name="display_name"/></div>
|
||||
</t>
|
||||
</templates></kanban>`,
|
||||
'product.product,false,search': '<search></search>',
|
||||
};
|
||||
|
||||
async function mockRPC(route, args) {
|
||||
const result = await this._super(...arguments);
|
||||
if (args.method === NAME_SEARCH && args.model === PRODUCT_PRODUCT) {
|
||||
const records = this.data[PRODUCT_PRODUCT].records
|
||||
.filter((record) => record.barcode === args.kwargs.name)
|
||||
.map((record) => [record.id, record.name]);
|
||||
return records.concat(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QUnit.module('web_mobile', {
|
||||
beforeEach() {
|
||||
this.data = {
|
||||
[PRODUCT_PRODUCT]: {
|
||||
fields: {
|
||||
id: {type: 'integer'},
|
||||
name: {},
|
||||
barcode: {},
|
||||
},
|
||||
records: [{
|
||||
id: 111,
|
||||
name: 'product_cable_management_box',
|
||||
barcode: '601647855631',
|
||||
}, {
|
||||
id: 112,
|
||||
name: 'product_n95_mask',
|
||||
barcode: '601647855632',
|
||||
}, {
|
||||
id: 113,
|
||||
name: 'product_surgical_mask',
|
||||
barcode: '601647855633',
|
||||
}],
|
||||
},
|
||||
[SALE_ORDER_LINE]: {
|
||||
fields: {
|
||||
id: {type: 'integer'},
|
||||
[PRODUCT_FIELD_NAME]: {
|
||||
string: PRODUCT_FIELD_NAME,
|
||||
type: 'many2one',
|
||||
relation: PRODUCT_PRODUCT
|
||||
},
|
||||
product_uom_qty: {type: 'integer'}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test("web_mobile: barcode button in a mobile environment with single results", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// simulate a mobile environment
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2OneBarcode);
|
||||
mock.patch(BarcodeScanner, {
|
||||
isBarcodeScannerSupported: () => true,
|
||||
scanBarcode: async () => this.data[PRODUCT_PRODUCT].records[0].barcode,
|
||||
});
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="${PRODUCT_FIELD_NAME}" widget="many2one_barcode"/>
|
||||
</sheet>
|
||||
</form>`,
|
||||
data: this.data,
|
||||
model: SALE_ORDER_LINE,
|
||||
archs: ARCHS,
|
||||
mockRPC,
|
||||
});
|
||||
|
||||
const $scanButton = form.$('.o_barcode');
|
||||
|
||||
assert.equal($scanButton.length, 1, "has scanner button");
|
||||
|
||||
await dom.click($scanButton);
|
||||
|
||||
const selectedId = form.renderer.state.data[PRODUCT_FIELD_NAME].res_id;
|
||||
assert.equal(selectedId, this.data[PRODUCT_PRODUCT].records[0].id,
|
||||
`product found and selected (${this.data[PRODUCT_PRODUCT].records[0].barcode})`);
|
||||
|
||||
form.destroy();
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2One);
|
||||
mock.unpatch(BarcodeScanner);
|
||||
});
|
||||
|
||||
QUnit.test("web_mobile: barcode button in a mobile environment with multiple results", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
// simulate a mobile environment
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2OneBarcode);
|
||||
mock.patch(BarcodeScanner, {
|
||||
isBarcodeScannerSupported: () => true,
|
||||
scanBarcode: async () => "mask",
|
||||
});
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="${PRODUCT_FIELD_NAME}" widget="many2one_barcode"/>
|
||||
</sheet>
|
||||
</form>`,
|
||||
data: this.data,
|
||||
model: SALE_ORDER_LINE,
|
||||
archs: ARCHS,
|
||||
mockRPC,
|
||||
});
|
||||
|
||||
const $scanButton = form.$('.o_barcode');
|
||||
|
||||
assert.equal($scanButton.length, 1, "has scanner button");
|
||||
|
||||
await dom.click($scanButton);
|
||||
|
||||
const $modal = $('.o_modal_full .modal-lg');
|
||||
assert.equal($modal.length, 1, 'there should be one modal opened in full screen');
|
||||
|
||||
assert.equal($modal.find('.o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)').length, 2,
|
||||
'there should be 2 records displayed');
|
||||
|
||||
await dom.click($modal.find('.o_legacy_kanban_view .o_kanban_record:first'));
|
||||
|
||||
const selectedId = form.renderer.state.data[PRODUCT_FIELD_NAME].res_id;
|
||||
assert.equal(selectedId, this.data[PRODUCT_PRODUCT].records[1].id,
|
||||
`product found and selected (${this.data[PRODUCT_PRODUCT].records[1].barcode})`);
|
||||
|
||||
form.destroy();
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2One);
|
||||
mock.unpatch(BarcodeScanner);
|
||||
});
|
||||
});
|
||||
});
|
||||
155
web_enterprise/static/tests/legacy/barcodes_tests.js
Normal file
155
web_enterprise/static/tests/legacy/barcodes_tests.js
Normal file
@@ -0,0 +1,155 @@
|
||||
odoo.define('web_mobile.barcode.tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const fieldRegistry = require('web.field_registry');
|
||||
const FormView = require('web.FormView');
|
||||
const { FieldMany2One } = require('web.relational_fields');
|
||||
const { createView, dom, mock } = require('web.test_utils');
|
||||
|
||||
const FieldMany2OneBarcode = require('web_mobile.barcode_fields');
|
||||
const BarcodeScanner = require('@web/webclient/barcode/barcode_scanner');
|
||||
|
||||
const NAME_SEARCH = "name_search";
|
||||
const PRODUCT_PRODUCT = 'product.product';
|
||||
const SALE_ORDER_LINE = 'sale_order_line';
|
||||
const PRODUCT_FIELD_NAME = 'product_id';
|
||||
const ARCHS = {
|
||||
'product.product,false,list': `<tree>
|
||||
<field name="display_name"/>
|
||||
</tree>`,
|
||||
'product.product,false,search': '<search></search>',
|
||||
};
|
||||
|
||||
async function mockRPC(route, args) {
|
||||
const result = await this._super(...arguments);
|
||||
if (args.method === NAME_SEARCH && args.model === PRODUCT_PRODUCT) {
|
||||
const records = this.data[PRODUCT_PRODUCT].records
|
||||
.filter((record) => record.barcode === args.kwargs.name)
|
||||
.map((record) => [record.id, record.name]);
|
||||
return records.concat(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QUnit.module('web_mobile', {
|
||||
beforeEach() {
|
||||
this.data = {
|
||||
[PRODUCT_PRODUCT]: {
|
||||
fields: {
|
||||
id: {type: 'integer'},
|
||||
name: {},
|
||||
barcode: {},
|
||||
},
|
||||
records: [{
|
||||
id: 111,
|
||||
name: 'product_cable_management_box',
|
||||
barcode: '601647855631',
|
||||
}, {
|
||||
id: 112,
|
||||
name: 'product_n95_mask',
|
||||
barcode: '601647855632',
|
||||
}, {
|
||||
id: 113,
|
||||
name: 'product_surgical_mask',
|
||||
barcode: '601647855633',
|
||||
}],
|
||||
},
|
||||
[SALE_ORDER_LINE]: {
|
||||
fields: {
|
||||
id: {type: 'integer'},
|
||||
[PRODUCT_FIELD_NAME]: {
|
||||
string: PRODUCT_FIELD_NAME,
|
||||
type: 'many2one',
|
||||
relation: PRODUCT_PRODUCT
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.test("web_mobile: barcode button in a mobile environment with single results", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// simulate a mobile environment
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2OneBarcode);
|
||||
mock.patch(BarcodeScanner, {
|
||||
isBarcodeScannerSupported: () => true,
|
||||
scanBarcode: async () => this.data[PRODUCT_PRODUCT].records[0].barcode,
|
||||
});
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="${PRODUCT_FIELD_NAME}" widget="many2one_barcode"/>
|
||||
</sheet>
|
||||
</form>`,
|
||||
data: this.data,
|
||||
model: SALE_ORDER_LINE,
|
||||
archs: ARCHS,
|
||||
mockRPC,
|
||||
});
|
||||
|
||||
const $scanButton = form.$('.o_barcode');
|
||||
|
||||
assert.containsOnce(form, $scanButton, "has scanner button");
|
||||
|
||||
await dom.click($scanButton);
|
||||
|
||||
const selectedId = form.renderer.state.data[PRODUCT_FIELD_NAME].res_id;
|
||||
assert.equal(selectedId, this.data[PRODUCT_PRODUCT].records[0].id,
|
||||
`product found and selected (${this.data[PRODUCT_PRODUCT].records[0].barcode})`);
|
||||
|
||||
form.destroy();
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2One);
|
||||
mock.unpatch(BarcodeScanner);
|
||||
});
|
||||
|
||||
QUnit.test("web_mobile: barcode button in a mobile environment with multiple results", async function (assert) {
|
||||
// simulate a mobile environment
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2OneBarcode);
|
||||
mock.patch(BarcodeScanner, {
|
||||
isBarcodeScannerSupported: () => true,
|
||||
scanBarcode: async () => "mask"
|
||||
});
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="${PRODUCT_FIELD_NAME}" widget="many2one_barcode"/>
|
||||
</sheet>
|
||||
</form>`,
|
||||
data: this.data,
|
||||
model: SALE_ORDER_LINE,
|
||||
archs: ARCHS,
|
||||
mockRPC,
|
||||
});
|
||||
|
||||
const $scanButton = form.$('.o_barcode');
|
||||
|
||||
assert.containsOnce(form, $scanButton, "has scanner button");
|
||||
|
||||
await dom.click($scanButton);
|
||||
|
||||
const $modal = $('.modal-dialog.modal-lg');
|
||||
assert.containsOnce($('body'), $modal, 'there should be one modal opened in full screen');
|
||||
|
||||
assert.containsN($modal, '.o_legacy_list_view .o_data_row', 2,
|
||||
'there should be 2 records displayed');
|
||||
|
||||
await dom.click($modal.find('.o_legacy_list_view .o_data_row:first'));
|
||||
|
||||
const selectedId = form.renderer.state.data[PRODUCT_FIELD_NAME].res_id;
|
||||
assert.equal(selectedId, this.data[PRODUCT_PRODUCT].records[1].id,
|
||||
`product found and selected (${this.data[PRODUCT_PRODUCT].records[1].barcode})`);
|
||||
|
||||
form.destroy();
|
||||
fieldRegistry.add('many2one_barcode', FieldMany2One);
|
||||
mock.unpatch(BarcodeScanner);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
odoo.define('web.action_menus_mobile_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const ActionMenus = require('web.ActionMenus');
|
||||
const Registry = require('web.Registry');
|
||||
const testUtils = require('web.test_utils');
|
||||
|
||||
const { createComponent } = testUtils;
|
||||
|
||||
QUnit.module('Components', {
|
||||
beforeEach() {
|
||||
this.action = {
|
||||
res_model: 'hobbit',
|
||||
};
|
||||
this.view = {
|
||||
type: 'form',
|
||||
};
|
||||
this.props = {
|
||||
activeIds: [23],
|
||||
context: {},
|
||||
items: {
|
||||
action: [
|
||||
{ action: { id: 1 }, name: "What's taters, precious ?", id: 1 },
|
||||
],
|
||||
print: [
|
||||
{ action: { id: 2 }, name: "Po-ta-toes", id: 2 },
|
||||
],
|
||||
},
|
||||
};
|
||||
// Patch the registry of the action menus
|
||||
this.actionMenusRegistry = ActionMenus.registry;
|
||||
ActionMenus.registry = new Registry();
|
||||
},
|
||||
afterEach() {
|
||||
ActionMenus.registry = this.actionMenusRegistry;
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.module('ActionMenus');
|
||||
|
||||
QUnit.test('Auto close the print dropdown after click inside an item', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const actionMenus = await createComponent(ActionMenus, {
|
||||
env: {
|
||||
device: {
|
||||
isMobile: true
|
||||
},
|
||||
action: this.action,
|
||||
view: this.view,
|
||||
},
|
||||
intercepts: {
|
||||
'do-action': ev => assert.step('do-action'),
|
||||
},
|
||||
props: this.props,
|
||||
async mockRPC(route, args) {
|
||||
switch (route) {
|
||||
case '/web/action/load':
|
||||
const expectedContext = {
|
||||
active_id: 23,
|
||||
active_ids: [23],
|
||||
active_model: 'hobbit',
|
||||
};
|
||||
assert.deepEqual(args.context, expectedContext);
|
||||
assert.step('load-action');
|
||||
return {context: {}, flags: {}};
|
||||
default:
|
||||
return this._super(...arguments);
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
await testUtils.controlPanel.toggleActionMenu(actionMenus, "Print");
|
||||
assert.containsOnce(actionMenus.el, '.dropdown-menu-start',
|
||||
"should display the dropdown menu");
|
||||
await testUtils.controlPanel.toggleMenuItem(actionMenus, "Po-ta-toes");
|
||||
assert.containsNone(actionMenus.el, '.dropdown-menu-start',
|
||||
"should not display the dropdown menu");
|
||||
assert.verifySteps(['load-action', 'do-action']);
|
||||
});
|
||||
});
|
||||
});
|
||||
245
web_enterprise/static/tests/legacy/control_panel_mobile_tests.js
Normal file
245
web_enterprise/static/tests/legacy/control_panel_mobile_tests.js
Normal file
@@ -0,0 +1,245 @@
|
||||
odoo.define('web.control_panel_mobile_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const FormView = require('web.FormView');
|
||||
const testUtils = require('web.test_utils');
|
||||
|
||||
const cpHelpers = require('@web/../tests/search/helpers');
|
||||
const { browser } = require("@web/core/browser/browser");
|
||||
const { patchWithCleanup, getFixture } = require("@web/../tests/helpers/utils");
|
||||
const { createControlPanel, createView } = testUtils;
|
||||
|
||||
const { createWebClient, doAction, getActionManagerServerData } = require('@web/../tests/webclient/helpers');
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
||||
QUnit.module('Control Panel', {
|
||||
beforeEach: function () {
|
||||
target = getFixture();
|
||||
this.actions = [{
|
||||
id: 1,
|
||||
name: "Yes",
|
||||
res_model: 'partner',
|
||||
type: 'ir.actions.act_window',
|
||||
views: [[false, 'list']],
|
||||
}];
|
||||
this.archs = {
|
||||
'partner,false,list': '<tree><field name="foo"/></tree>',
|
||||
'partner,false,search': `
|
||||
<search>
|
||||
<filter string="Active" name="my_projects" domain="[('boolean_field', '=', True)]"/>
|
||||
<field name="foo" string="Foo"/>
|
||||
</search>`,
|
||||
};
|
||||
this.data = {
|
||||
partner: {
|
||||
fields: {
|
||||
foo: { string: "Foo", type: "char" },
|
||||
boolean_field: { string: "I am a boolean", type: "boolean" },
|
||||
},
|
||||
records: [
|
||||
{ id: 1, display_name: "First record", foo: "yop" },
|
||||
],
|
||||
},
|
||||
};
|
||||
const actions = {};
|
||||
this.actions.forEach((act) => {
|
||||
actions[act.xml_id || act.id] = act;
|
||||
});
|
||||
serverData = getActionManagerServerData();
|
||||
Object.assign(serverData, { models: this.data, views: this.archs, actions });
|
||||
patchWithCleanup(browser, {
|
||||
setTimeout: (fn) => fn(),
|
||||
clearTimeout: () => {},
|
||||
});
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.test('basic rendering', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const webClient = await createWebClient({serverData});
|
||||
|
||||
await doAction(webClient, 1);
|
||||
|
||||
assert.containsNone(target, '.o_control_panel .o_mobile_search',
|
||||
"search options are hidden by default");
|
||||
assert.containsOnce(target, '.o_control_panel .o_enable_searchview',
|
||||
"should display a button to toggle the searchview");
|
||||
});
|
||||
|
||||
QUnit.test("control panel appears at top on scroll event", async function (assert) {
|
||||
assert.expect(12);
|
||||
|
||||
const MAX_HEIGHT = 800;
|
||||
const MIDDLE_HEIGHT = 400;
|
||||
const DELTA_TEST = 20;
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
`<div style="height: ${2 * MAX_HEIGHT}px"></div>` +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
data: this.data,
|
||||
model: 'partner',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
const controlPanelEl = document.querySelector('.o_control_panel');
|
||||
const controlPanelHeight = controlPanelEl.offsetHeight;
|
||||
|
||||
// Force container to have a scrollbar
|
||||
controlPanelEl.parentElement.style.maxHeight = `${MAX_HEIGHT}px`;
|
||||
|
||||
const scrollAndAssert = async (targetHeight, expectedTopValue, hasStickyClass) => {
|
||||
if (targetHeight !== null) {
|
||||
controlPanelEl.parentElement.scrollTo(0, targetHeight);
|
||||
await testUtils.nextTick();
|
||||
}
|
||||
const expectedPixelValue = `${expectedTopValue}px`;
|
||||
assert.strictEqual(controlPanelEl.style.top, expectedPixelValue,
|
||||
`Top must be ${expectedPixelValue} (after scroll to ${targetHeight})`);
|
||||
|
||||
if (hasStickyClass) {
|
||||
assert.hasClass(controlPanelEl, 'o_mobile_sticky');
|
||||
} else {
|
||||
assert.doesNotHaveClass(controlPanelEl, 'o_mobile_sticky');
|
||||
}
|
||||
}
|
||||
|
||||
// Initial position (scrollTop: 0)
|
||||
await scrollAndAssert(null, 0, false);
|
||||
|
||||
// Scroll down 800px (scrollTop: 800)
|
||||
await scrollAndAssert(MAX_HEIGHT, -controlPanelHeight, true);
|
||||
|
||||
// Scoll up 20px (scrollTop: 780)
|
||||
await scrollAndAssert(MAX_HEIGHT - DELTA_TEST, -controlPanelHeight + DELTA_TEST, true);
|
||||
|
||||
// Scroll up 380px (scrollTop: 400)
|
||||
await scrollAndAssert(MIDDLE_HEIGHT, 0, true);
|
||||
|
||||
// Scroll down 200px (scrollTop: 800)
|
||||
await scrollAndAssert(MAX_HEIGHT, -controlPanelHeight, true);
|
||||
|
||||
// Scroll up 400px (scrollTop: 0)
|
||||
await scrollAndAssert(0, -controlPanelHeight, false);
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("mobile search: basic display", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const fields = {
|
||||
birthday: { string: "Birthday", type: "date", store: true, sortable: true },
|
||||
};
|
||||
const searchMenuTypes = ["filter", "groupBy", "comparison", "favorite"];
|
||||
const params = {
|
||||
cpModelConfig: {
|
||||
arch: `
|
||||
<search>
|
||||
<filter name="birthday" date="birthday"/>
|
||||
</search>`,
|
||||
fields,
|
||||
searchMenuTypes,
|
||||
},
|
||||
cpProps: { fields, searchMenuTypes },
|
||||
};
|
||||
const controlPanel = await createControlPanel(params);
|
||||
|
||||
// Toggle search bar controls
|
||||
await testUtils.dom.click(controlPanel.el.querySelector("button.o_enable_searchview"));
|
||||
// Open search view
|
||||
await testUtils.dom.click(controlPanel.el.querySelector("button.o_toggle_searchview_full"));
|
||||
|
||||
// Toggle filter date
|
||||
// Note: 'document.body' is used instead of 'controlPanel' because the
|
||||
// search view is directly in the body.
|
||||
await cpHelpers.toggleFilterMenu(document);
|
||||
await cpHelpers.toggleMenuItem(document, "Birthday");
|
||||
await cpHelpers.toggleMenuItemOption(document, "Birthday", 0);
|
||||
|
||||
assert.containsOnce(document.body, ".o_filter_menu");
|
||||
assert.containsOnce(document.body, ".o_group_by_menu");
|
||||
assert.containsOnce(document.body, ".o_comparison_menu");
|
||||
assert.containsOnce(document.body, ".o_favorite_menu");
|
||||
});
|
||||
|
||||
QUnit.test('mobile search: activate a filter through quick search', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
let searchRPCFlag = false;
|
||||
|
||||
const mockRPC = (route, args) => {
|
||||
if (searchRPCFlag && args.method === "web_search_read") {
|
||||
assert.deepEqual(args.kwargs.domain, [['foo', 'ilike', 'A']],
|
||||
"domain should have been properly transferred to list view");
|
||||
}
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({serverData, mockRPC});
|
||||
|
||||
await doAction(webClient, 1);
|
||||
|
||||
assert.containsOnce(document.body, 'button.o_enable_searchview.oi-search',
|
||||
"should display a button to open the searchview");
|
||||
assert.containsNone(document.body, '.o_searchview_input_container',
|
||||
"Quick search input should be hidden");
|
||||
|
||||
// open the search view
|
||||
await testUtils.dom.click(document.querySelector('button.o_enable_searchview'));
|
||||
|
||||
assert.containsOnce(document.body, '.o_toggle_searchview_full',
|
||||
"should display a button to expand the searchview");
|
||||
assert.containsOnce(document.body, '.o_searchview_input_container',
|
||||
"Quick search input should now be visible");
|
||||
|
||||
searchRPCFlag = true;
|
||||
|
||||
// use quick search input (search view is directly put in the body)
|
||||
await cpHelpers.editSearch(document.body, "A");
|
||||
await cpHelpers.validateSearch(document.body);
|
||||
|
||||
// close quick search
|
||||
await testUtils.dom.click(document.querySelector('button.o_enable_searchview.fa-arrow-left'));
|
||||
|
||||
assert.containsNone(document.body, '.o_toggle_searchview_full',
|
||||
"Expand icon shoud be hidden");
|
||||
assert.containsNone(document.body, '.o_searchview_input_container',
|
||||
"Quick search input should be hidden");
|
||||
});
|
||||
|
||||
QUnit.test('mobile search: activate a filter in full screen search view', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, 1);
|
||||
|
||||
assert.containsNone(document.body, '.o_mobile_search');
|
||||
|
||||
// open the search view
|
||||
await testUtils.dom.click(target.querySelector('button.o_enable_searchview'));
|
||||
// open it in full screen
|
||||
await testUtils.dom.click(target.querySelector('.o_toggle_searchview_full'));
|
||||
|
||||
assert.containsOnce(document.body, '.o_mobile_search');
|
||||
|
||||
await cpHelpers.toggleFilterMenu(document.body);
|
||||
await cpHelpers.toggleMenuItem(document.body, "Active");
|
||||
|
||||
// closing search view
|
||||
await testUtils.dom.click(
|
||||
[...document.querySelectorAll('.o_mobile_search_button')].find(
|
||||
e => e.innerText.trim() === "FILTER"
|
||||
)
|
||||
);
|
||||
assert.containsNone(document.body, '.o_mobile_search');
|
||||
});
|
||||
});
|
||||
});
|
||||
409
web_enterprise/static/tests/legacy/form_mobile_tests.js
Normal file
409
web_enterprise/static/tests/legacy/form_mobile_tests.js
Normal file
@@ -0,0 +1,409 @@
|
||||
odoo.define('web_enterprise.form_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var FormView = require('web.FormView');
|
||||
var testUtils = require('web.test_utils');
|
||||
|
||||
var createView = testUtils.createView;
|
||||
|
||||
QUnit.module('web_enterprise', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
partner: {
|
||||
fields: {
|
||||
display_name: { string: "Displayed name", type: "char" },
|
||||
trululu: {string: "Trululu", type: "many2one", relation: 'partner'},
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
display_name: "first record",
|
||||
trululu: 4,
|
||||
}, {
|
||||
id: 2,
|
||||
display_name: "second record",
|
||||
trululu: 1,
|
||||
}, {
|
||||
id: 4,
|
||||
display_name: "aaa",
|
||||
}],
|
||||
},
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.module('Mobile FormView');
|
||||
|
||||
QUnit.test('statusbar buttons are correctly rendered in mobile', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form string="Partners">' +
|
||||
'<header>' +
|
||||
'<button string="Confirm"/>' +
|
||||
'<button string="Do it"/>' +
|
||||
'</header>' +
|
||||
'<sheet>' +
|
||||
'<group>' +
|
||||
'<button name="display_name"/>' +
|
||||
'</group>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
assert.strictEqual(form.$('.o_statusbar_buttons a:contains(Action)').length, 1,
|
||||
"statusbar should contain a button 'Action'");
|
||||
assert.containsOnce(form, '.o_statusbar_buttons .dropdown-menu',
|
||||
"statusbar should contain a dropdown");
|
||||
assert.containsNone(form, '.o_statusbar_buttons .dropdown-menu:visible',
|
||||
"dropdown should be hidden");
|
||||
|
||||
// open the dropdown
|
||||
await testUtils.dom.click(form.$('.o_statusbar_buttons a'));
|
||||
assert.containsOnce(form, '.o_statusbar_buttons .dropdown-menu:visible',
|
||||
"dropdown should be visible");
|
||||
assert.containsN(form, '.o_statusbar_buttons .dropdown-menu > button', 2,
|
||||
"dropdown should contain 2 buttons");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('statusbar "Action" button should be displayed only if there are multiple visible buttons', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form>' +
|
||||
'<header>' +
|
||||
'<button string="Confirm" attrs=\'{"invisible": [["display_name", "=", "first record"]]}\'/>' +
|
||||
'<button string="Do it" attrs=\'{"invisible": [["display_name", "=", "first record"]]}\'/>' +
|
||||
'</header>' +
|
||||
'<sheet>' +
|
||||
'<group>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</group>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
// if all buttons are invisible then there should be no action button
|
||||
assert.containsNone(form, '.o_statusbar_buttons > btn-group > .dropdown-toggle',
|
||||
"'Action' dropdown is not displayed as there are no visible buttons");
|
||||
// there should be two invisible buttons
|
||||
assert.containsN(form, '.o_statusbar_buttons > button.o_invisible_modifier', 2,
|
||||
"Status bar should have two buttons with 'o_invisible_modifier' class");
|
||||
|
||||
// change display_name to update buttons modifiers and make it visible
|
||||
await testUtils.form.clickEdit(form);
|
||||
await testUtils.fields.editInput(form.$('input[name=display_name]'), 'test');
|
||||
await testUtils.form.clickSave(form);
|
||||
assert.containsOnce(form, '.o_statusbar_buttons a:contains(Action)',
|
||||
"statusbar should contain a button 'Action'");
|
||||
assert.containsOnce(form, '.o_statusbar_buttons .dropdown-menu',
|
||||
"statusbar should contain a dropdown");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('statusbar "Action" button not displayed in edit mode with .oe_read_only button', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<header>
|
||||
<button string="Share" type="action" class="oe_highlight oe_read_only"/>
|
||||
<button string="Email" type="action" class="oe_highlight oe_read_only"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="display_name"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
res_id: 1,
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsNone(form, '.o_statusbar_buttons a:contains(Action)',
|
||||
"'Action' button should not be there");
|
||||
await testUtils.form.clickSave(form);
|
||||
assert.containsOnce(form, '.o_statusbar_buttons a:contains(Action)',
|
||||
"'Action' button should be there");
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test(`statusbar "Action" button shouldn't be displayed for only one visible button`, async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `<form>
|
||||
<header>
|
||||
<button string="Hola" attrs='{"invisible": [["display_name", "=", "first record"]]}'/>
|
||||
<button string="Ciao"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="display_name"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>`,
|
||||
res_id: 1,
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
||||
// There should be a simple statusbar button and no action dropdown
|
||||
assert.containsNone(form, '.o_statusbar_buttons a:contains(Action)', "should have no 'Action' dropdown");
|
||||
assert.containsOnce(form, '.o_statusbar_buttons > button > span:contains(Ciao)', "should have a simple statusbar button 'Ciao'");
|
||||
|
||||
// change display_name to update buttons modifiers and make both buttons visible
|
||||
await testUtils.fields.editInput(form.$('input[name=display_name]'), 'test');
|
||||
|
||||
// Now there should an action dropdown, because there are two visible buttons
|
||||
assert.containsOnce(form, '.o_statusbar_buttons a:contains(Action)', "should have no 'Action' dropdown");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test(`statusbar widgets should appear in the statusbar dropdown only if there are multiple items`, async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form string="Partners">
|
||||
<header>
|
||||
<widget name="attach_document" string="Attach document"/>
|
||||
<button string="Ciao" attrs='{"invisible": [["display_name", "=", "first record"]]}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="display_name"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
res_id: 2,
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
||||
const dropdownActionButton = '.o_statusbar_buttons a:contains(Action)';
|
||||
|
||||
// Now there should an action dropdown, because there are two visible buttons
|
||||
assert.containsOnce(form, dropdownActionButton, "should have 'Action' dropdown");
|
||||
|
||||
assert.containsN(form, `.o_statusbar_buttons .dropdown-menu > button`,
|
||||
2, "should have 2 buttons in the dropdown");
|
||||
|
||||
// change display_name to update buttons modifiers and make one button visible
|
||||
await testUtils.fields.editInput(form.$('input[name=display_name]'), 'first record');
|
||||
|
||||
// There should be a simple statusbar button and no action dropdown
|
||||
assert.containsNone(form, dropdownActionButton, "shouldn't have 'Action' dropdown");
|
||||
assert.containsOnce(form, `.o_statusbar_buttons > button:visible`,
|
||||
"should have 1 button visible in the statusbar");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test(`Quick Edition: quick edit many2one`, async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="trululu" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
archs: {
|
||||
'partner,false,kanban': `
|
||||
<kanban>
|
||||
<templates><t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click">
|
||||
<field name="display_name"/>
|
||||
</div>
|
||||
</t></templates>
|
||||
</kanban>
|
||||
`,
|
||||
'partner,false,search': '<search></search>',
|
||||
},
|
||||
res_id: 2,
|
||||
});
|
||||
|
||||
await testUtils.dom.click(form.$('.o_form_label'));
|
||||
await testUtils.nextTick(); // wait for quick edit
|
||||
|
||||
const $modal = $('.o_modal_full .modal-lg');
|
||||
assert.equal($modal.length, 1, 'there should be one modal opened in full screen');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('statusbar "Action" dropdown should keep its open/close state', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<header>
|
||||
<button string="Just more than one"/>
|
||||
<button string="Confirm" attrs='{"invisible": [["display_name", "=", ""]]}'/>
|
||||
<button string="Do it" attrs='{"invisible": [["display_name", "!=", ""]]}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<field name="display_name"/>
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
});
|
||||
|
||||
const dropdownMenuSelector = '.o_statusbar_buttons .dropdown-menu';
|
||||
assert.containsOnce(form, dropdownMenuSelector,
|
||||
"statusbar should contain a dropdown");
|
||||
assert.doesNotHaveClass(form.el.querySelector(dropdownMenuSelector), 'show',
|
||||
"dropdown should be closed");
|
||||
|
||||
// open the dropdown
|
||||
await testUtils.dom.click(form.el.querySelector('.o_statusbar_buttons .dropdown-toggle'));
|
||||
assert.hasClass(form.el.querySelector(dropdownMenuSelector), 'show',
|
||||
"dropdown should be opened");
|
||||
|
||||
// change display_name to update buttons' modifiers
|
||||
await testUtils.fields.editInput(form.el.querySelector('input[name="display_name"]'), 'test');
|
||||
assert.containsOnce(form, dropdownMenuSelector,
|
||||
"statusbar should contain a dropdown");
|
||||
assert.hasClass(form.el.querySelector(dropdownMenuSelector), 'show',
|
||||
"dropdown should still be opened");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test(`statusbar "Action" dropdown's open/close state shouldn't be modified after 'onchange'`, async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
let resolveOnchange;
|
||||
const onchangePromise = new Promise(resolve => {
|
||||
resolveOnchange = resolve;
|
||||
});
|
||||
|
||||
this.data.partner.onchanges = {
|
||||
display_name: async () => {},
|
||||
};
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<header>
|
||||
<button name="create" string="Create Invoice" type="action"/>
|
||||
<button name="send" string="Send by Email" type="action"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<field name="display_name" />
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
async mockRPC(route, { method, args: [, , changedField] }) {
|
||||
return method === 'onchange' && changedField === 'display_name' ? onchangePromise : this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
const dropdownMenuSelector = '.o_statusbar_buttons .dropdown-menu';
|
||||
assert.containsOnce(form, dropdownMenuSelector,
|
||||
"statusbar should contain a dropdown");
|
||||
assert.doesNotHaveClass(form.el.querySelector(dropdownMenuSelector), 'show',
|
||||
"dropdown should be closed");
|
||||
|
||||
await testUtils.fields.editInput(form.el.querySelector('input[name="display_name"]'), 'before onchange');
|
||||
await testUtils.dom.click(form.el.querySelector('.o_statusbar_buttons .dropdown-toggle'));
|
||||
assert.hasClass(form.el.querySelector(dropdownMenuSelector), 'show',
|
||||
"dropdown should be opened");
|
||||
resolveOnchange({ value: { display_name: 'after onchange' } });
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.el.querySelector('input[name="display_name"]').value, 'after onchange');
|
||||
assert.hasClass(form.el.querySelector(dropdownMenuSelector), 'show',
|
||||
"dropdown should still be opened");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("preserve current scroll position on form view while closing dialog", async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch: `<form>
|
||||
<sheet>
|
||||
<p style='height:500px'></p>
|
||||
<field name="trululu"/>
|
||||
<p style='height:500px'></p>
|
||||
</sheet>
|
||||
</form>`,
|
||||
archs: {
|
||||
"partner,false,kanban": `<kanban>
|
||||
<templates><t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click"><field name="display_name"/></div>
|
||||
</t></templates>
|
||||
</kanban>`,
|
||||
"partner,false,search": "<search></search>",
|
||||
},
|
||||
data: this.data,
|
||||
model: "partner",
|
||||
res_id: 2,
|
||||
debug: true, // need to be in the viewport to check scrollPosition
|
||||
viewOptions: { mode: "edit" },
|
||||
});
|
||||
|
||||
const scrollPosition = { top: 265, left: 0 };
|
||||
window.scrollTo(scrollPosition);
|
||||
|
||||
assert.strictEqual(window.scrollY, scrollPosition.top, "Should have scrolled 265 px vertically");
|
||||
assert.strictEqual(window.scrollX, scrollPosition.left, "Should be 0 px from left as it is");
|
||||
// click on m2o field
|
||||
await testUtils.dom.click(form.$(".o_field_many2one input"));
|
||||
assert.strictEqual(window.scrollY, 0, "Should have scrolled to top (0) px");
|
||||
assert.containsOnce($("body"), ".modal.o_modal_full", "there should be a many2one modal opened in full screen");
|
||||
// click on back button
|
||||
await testUtils.dom.click($(".modal").find(".modal-header .fa-arrow-left"));
|
||||
assert.strictEqual(window.scrollY, scrollPosition.top, "Should have scrolled back to 265 px vertically");
|
||||
assert.strictEqual(window.scrollX, scrollPosition.left, "Should be 0 px from left as it is");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,498 @@
|
||||
odoo.define('web_enterprise.relational_fields_mobile_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var FormView = require('web.FormView');
|
||||
var testUtils = require('web.test_utils');
|
||||
|
||||
var createView = testUtils.createView;
|
||||
|
||||
QUnit.module('web_enterprise', {}, function () {
|
||||
|
||||
QUnit.module('relational_fields', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
partner: {
|
||||
fields: {
|
||||
display_name: { string: "Displayed name", type: "char" },
|
||||
trululu: {string: "Trululu", type: "many2one", relation: 'partner'},
|
||||
sibling_ids: {string: "Sibling", type: "many2many", relation: 'partner'},
|
||||
p: { string: "one2many field", type: "one2many", relation: 'partner', relation_field: 'trululu' },
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
display_name: "first record",
|
||||
trululu: 4,
|
||||
}, {
|
||||
id: 2,
|
||||
display_name: "second record",
|
||||
trululu: 1,
|
||||
}, {
|
||||
id: 4,
|
||||
display_name: "aaa",
|
||||
}],
|
||||
},
|
||||
};
|
||||
}
|
||||
}, function () {
|
||||
|
||||
QUnit.module('FieldStatus');
|
||||
|
||||
QUnit.test('statusbar is rendered correclty on small devices', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:
|
||||
'<form string="Partners">' +
|
||||
'<header><field name="trululu" widget="statusbar"/></header>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
assert.strictEqual(form.$('.o_statusbar_status > button:contains(aaa)').length, 1,
|
||||
"should have only one visible status in mobile, the active one");
|
||||
assert.containsOnce(form, '.o_statusbar_status .dropdown-menu',
|
||||
"should have a dropdown containing all status");
|
||||
assert.containsNone(form, '.o_statusbar_status .dropdown-menu:visible',
|
||||
"dropdown should be hidden");
|
||||
|
||||
// open the dropdown
|
||||
testUtils.dom.click(form.$('.o_statusbar_status > button'));
|
||||
assert.containsOnce(form, '.o_statusbar_status .dropdown-menu:visible',
|
||||
"dropdown should be visible");
|
||||
assert.containsN(form, '.o_statusbar_status .dropdown-menu button', 3,
|
||||
"should have 3 status");
|
||||
assert.containsN(form, '.o_statusbar_status button:disabled', 3,
|
||||
"all status should be disabled");
|
||||
var $activeStatus = form.$('.o_statusbar_status .dropdown-menu button[data-value=4]');
|
||||
assert.hasClass($activeStatus,'btn-primary', "active status should be btn-primary");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('statusbar with no status on extra small screens', async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<header><field name="trululu" widget="statusbar"/></header>' +
|
||||
'</form>',
|
||||
res_id: 4,
|
||||
});
|
||||
|
||||
assert.hasClass(form.$('.o_statusbar_status'),'o_field_empty',
|
||||
'statusbar widget should have class o_field_empty');
|
||||
assert.strictEqual(form.$('.o_statusbar_status').children().length, 2,
|
||||
'statusbar widget should have two children');
|
||||
assert.containsOnce(form, '.o_statusbar_status button.dropdown-toggle',
|
||||
'statusbar widget should have a button');
|
||||
assert.strictEqual(form.$('.o_statusbar_status button.dropdown-toggle').text().trim(), '',
|
||||
'statusbar button has no text'); // Behavior as of saas-15, might be improved
|
||||
assert.containsOnce(form, '.o_statusbar_status .dropdown-menu',
|
||||
'statusbar widget should have a dropdown menu');
|
||||
assert.containsN(form, '.o_statusbar_status .dropdown-menu button', 3,
|
||||
'statusbar widget dropdown menu should have 3 buttons');
|
||||
assert.strictEqual(form.$('.o_statusbar_status .dropdown-menu button').eq(0).text().trim(), 'first record',
|
||||
'statusbar widget dropdown first button should display the first record display_name');
|
||||
assert.strictEqual(form.$('.o_statusbar_status .dropdown-menu button').eq(1).text().trim(), 'second record',
|
||||
'statusbar widget dropdown second button should display the second record display_name');
|
||||
assert.strictEqual(form.$('.o_statusbar_status .dropdown-menu button').eq(2).text().trim(), 'aaa',
|
||||
'statusbar widget dropdown third button should display the third record display_name');
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('clickable statusbar widget on mobile view', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<header><field name="trululu" widget="statusbar" options=\'{"clickable": "1"}\'/></header>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
var $selectedStatus = form.$('.o_statusbar_status button[data-value="4"]');
|
||||
assert.hasClass($selectedStatus, 'btn-primary');
|
||||
assert.hasClass($selectedStatus,'disabled');
|
||||
var selector = '.o_statusbar_status button.btn-secondary:not(.dropdown-toggle):not(:disabled)';
|
||||
assert.containsN(form, selector, 2, "other status should be btn-secondary and not disabled");
|
||||
|
||||
await testUtils.dom.click(form.$('.o_statusbar_status .dropdown-toggle'));
|
||||
await testUtils.dom.clickFirst(form.$(selector));
|
||||
|
||||
var $status = form.$('.o_statusbar_status button[data-value="1"]');
|
||||
assert.hasClass($status, 'btn-primary');
|
||||
assert.hasClass($status, 'disabled');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.module('FieldMany2One');
|
||||
|
||||
QUnit.test("many2one in a enterprise environment", async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="trululu"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'partner,false,kanban': '<kanban>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div class="oe_kanban_global_click"><field name="display_name"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
'partner,false,search': '<search></search>',
|
||||
},
|
||||
data: this.data,
|
||||
model: 'partner',
|
||||
res_id: 2,
|
||||
viewOptions: {mode: 'edit'},
|
||||
});
|
||||
|
||||
var $input = form.$('.o_field_many2one input');
|
||||
|
||||
assert.doesNotHaveClass($input, 'ui-autocomplete-input',
|
||||
"autocomplete should not be visible in a mobile environment");
|
||||
|
||||
await testUtils.dom.click($input);
|
||||
|
||||
var $modal = $('.o_modal_full .modal-lg');
|
||||
assert.equal($modal.length, 1, 'there should be one modal opened in full screen');
|
||||
assert.containsOnce($modal, '.o_legacy_kanban_view',
|
||||
'kanban view should be open in SelectCreateDialog');
|
||||
assert.containsOnce($modal, '.o_cp_searchview',
|
||||
'should have Search view inside SelectCreateDialog');
|
||||
assert.containsNone($modal.find(".o_control_panel .o_cp_buttons"), '.o-kanban-button-new',
|
||||
"kanban view in SelectCreateDialog should not have Create button");
|
||||
assert.strictEqual($modal.find(".o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)").length, 3,
|
||||
"popup should load 3 records in kanban");
|
||||
|
||||
await testUtils.dom.click($modal.find('.o_legacy_kanban_view .o_kanban_record:first'));
|
||||
|
||||
assert.strictEqual($input.val(), 'first record',
|
||||
'clicking kanban card should select record for many2one field');
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("hide/show element using selection_mode in kanban view in a enterprise environment", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
this.data.partner.fields.foo = {string: "Foo", type: "char", default: "My little Foo Value"};
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="trululu"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'partner,false,kanban': '<kanban>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div class="oe_kanban_global_click">' +
|
||||
'<field name="display_name"/>' +
|
||||
'</div>' +
|
||||
'<div class="o_sibling_tags" t-if="!selection_mode">' +
|
||||
'<field name="sibling_ids"/>' +
|
||||
'</div>' +
|
||||
'<div class="o_foo" t-if="selection_mode">' +
|
||||
'<field name="foo"/>' +
|
||||
'</div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
'partner,false,search': '<search></search>',
|
||||
},
|
||||
data: this.data,
|
||||
model: 'partner',
|
||||
res_id: 2,
|
||||
viewOptions: {mode: 'edit'},
|
||||
});
|
||||
|
||||
var $input = form.$('.o_field_many2one input');
|
||||
|
||||
assert.doesNotHaveClass($input, 'ui-autocomplete-input',
|
||||
"autocomplete should not be visible in a mobile environment");
|
||||
|
||||
await testUtils.dom.click($input);
|
||||
|
||||
var $modal = $('.o_modal_full .modal-lg');
|
||||
assert.equal($modal.length, 1, 'there should be one modal opened in full screen');
|
||||
assert.containsOnce($modal, '.o_legacy_kanban_view',
|
||||
'kanban view should be open in SelectCreateDialog');
|
||||
assert.containsNone($modal, '.o_legacy_kanban_view .o_sibling_tags',
|
||||
'o_sibling_tags div should not be available as div have condition on selection_mode');
|
||||
assert.containsN($modal, '.o_legacy_kanban_view .o_foo', 3,
|
||||
'o_foo div should be available as div have condition on selection_mode');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("kanban_view_ref attribute opens specific kanban view given as a reference in a mobile environment", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="trululu" kanban_view_ref="2"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'partner,1,kanban': '<kanban class="kanban1">' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div class="oe_kanban_global_click">' +
|
||||
'<field name="display_name"/>' +
|
||||
'</div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
'partner,2,kanban': '<kanban class="kanban2">' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div class="oe_kanban_global_click">' +
|
||||
'<div>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</div>' +
|
||||
'<div>' +
|
||||
'<field name="trululu"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
'partner,false,search': '<search></search>',
|
||||
},
|
||||
data: this.data,
|
||||
model: 'partner',
|
||||
res_id: 2,
|
||||
viewOptions: {mode: 'edit'},
|
||||
});
|
||||
|
||||
var $input = form.$('.o_field_many2one input');
|
||||
|
||||
assert.doesNotHaveClass($input, 'ui-autocomplete-input',
|
||||
"autocomplete should not be visible in a mobile environment");
|
||||
|
||||
await testUtils.dom.click($input);
|
||||
|
||||
var $modal = $('.o_modal_full .modal-lg');
|
||||
assert.equal($modal.length, 1, 'there should be one modal opened in full screen');
|
||||
assert.containsOnce($modal, '.o_legacy_kanban_view',
|
||||
'kanban view should be open in SelectCreateDialog');
|
||||
assert.hasClass($modal.find('.o_legacy_kanban_view'), 'kanban2',
|
||||
'kanban view with id 2 should be opened as it is given as kanban_view_ref');
|
||||
assert.strictEqual($modal.find('.o_legacy_kanban_view .o_kanban_record:first').text(),
|
||||
'first recordaaa',
|
||||
'kanban with two fields should be opened');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("many2one dialog on mobile: clear button header", async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="trululu"/>
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
archs: {
|
||||
'partner,false,kanban': `
|
||||
<kanban>
|
||||
<templates><t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click"><field name="display_name"/></div>
|
||||
</t></templates>
|
||||
</kanban>
|
||||
`,
|
||||
'partner,false,search': '<search></search>',
|
||||
},
|
||||
data: this.data,
|
||||
model: 'partner',
|
||||
res_id: 2,
|
||||
viewOptions: {mode: 'edit'},
|
||||
});
|
||||
|
||||
let $input = form.$('.o_field_many2one input');
|
||||
|
||||
assert.doesNotHaveClass($input, 'ui-autocomplete-input',
|
||||
"autocomplete should not be visible in a mobile environment");
|
||||
|
||||
await testUtils.dom.click($input);
|
||||
assert.containsOnce($('body'), '.modal.o_modal_full',
|
||||
"there should be a modal opened in full screen");
|
||||
assert.containsN($('.modal'), '.o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3,
|
||||
"popup should load 3 records in kanban");
|
||||
|
||||
await testUtils.dom.click($('.modal').find('.o_legacy_kanban_view .o_kanban_record:first'));
|
||||
assert.strictEqual($input.val(), 'first record',
|
||||
'clicking kanban card should select record for many2one field');
|
||||
|
||||
await testUtils.dom.click($input);
|
||||
// clear button.
|
||||
assert.containsOnce($('.modal').find('.modal-header'), '.o_clear_button',
|
||||
"there should be a Clear button in the modal header");
|
||||
|
||||
await testUtils.dom.click($('.modal').find('.modal-header .o_clear_button'));
|
||||
assert.containsNone($('body'), '.modal', "there should be no more modal");
|
||||
|
||||
$input = form.$('.o_field_many2one input');
|
||||
assert.strictEqual($input.val(), "", "many2one should be cleared");
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.module('FieldMany2Many');
|
||||
|
||||
QUnit.test("many2many_tags in a mobile environment", async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var rpcReadCount = 0;
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="sibling_ids" widget="many2many_tags"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'partner,false,kanban': '<kanban>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div class="oe_kanban_global_click"><field name="display_name"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
'partner,false,search': '<search></search>',
|
||||
},
|
||||
data: this.data,
|
||||
model: 'partner',
|
||||
res_id: 2,
|
||||
viewOptions: {mode: 'edit'},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "read" && args.model === "partner") {
|
||||
if (rpcReadCount === 0) {
|
||||
assert.deepEqual(args.args[0], [2], "form should initially show partner 2");
|
||||
} else if (rpcReadCount === 1) {
|
||||
assert.deepEqual(args.args[0], [1], "partner with id 1 should be selected");
|
||||
}
|
||||
rpcReadCount++;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
var $input = form.$(".o_field_widget .o_input");
|
||||
|
||||
assert.strictEqual($input.find(".badge").length, 0,
|
||||
"many2many_tags should have no tags");
|
||||
|
||||
await testUtils.dom.click($input);
|
||||
|
||||
var $modal = $('.o_modal_full .modal-lg');
|
||||
assert.equal($modal.length, 1, 'there should be one modal opened in full screen');
|
||||
assert.containsOnce($modal, '.o_legacy_kanban_view',
|
||||
'kanban view should be open in SelectCreateDialog');
|
||||
assert.containsOnce($modal, '.o_cp_searchview',
|
||||
'should have Search view inside SelectCreateDialog');
|
||||
assert.containsNone($modal.find(".o_control_panel .o_cp_buttons"), '.o-kanban-button-new',
|
||||
"kanban view in SelectCreateDialog should not have Create button");
|
||||
assert.strictEqual($modal.find(".o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)").length, 3,
|
||||
"popup should load 3 records in kanban");
|
||||
|
||||
await testUtils.dom.click($modal.find('.o_legacy_kanban_view .o_kanban_record:first'));
|
||||
|
||||
assert.strictEqual(rpcReadCount, 2, "there should be a read for current form record and selected sibling");
|
||||
assert.strictEqual(form.$(".o_field_widget.o_input .badge").length, 1,
|
||||
"many2many_tags should have partner coucou3");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.module('FieldOne2Many');
|
||||
|
||||
QUnit.test('one2many on mobile: remove header button', async function (assert) {
|
||||
assert.expect(9);
|
||||
this.data.partner.records[0].p = [1, 2, 4];
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form string="Partners">
|
||||
<field name="p"/>
|
||||
</form>
|
||||
`,
|
||||
archs: {
|
||||
'partner,false,form': `
|
||||
<form string="Partner">
|
||||
<field name="display_name"/>
|
||||
</form>
|
||||
`,
|
||||
'partner,false,kanban': `
|
||||
<kanban>
|
||||
<templates><t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click">
|
||||
<field name="display_name"/>
|
||||
</div>
|
||||
</t></templates>
|
||||
</kanban>
|
||||
`,
|
||||
},
|
||||
res_id: 1,
|
||||
mockRPC(route, args) {
|
||||
if (route === '/web/dataset/call_kw/partner/write') {
|
||||
const commands = args.args[1].p;
|
||||
assert.strictEqual(commands.length, 3,
|
||||
'should have generated three commands');
|
||||
assert.ok(commands[0][0] === 4 && commands[0][1] === 2,
|
||||
'should have generated the command 4 (LINK_TO) with id 2');
|
||||
assert.ok(commands[1][0] === 4 && commands[1][1] === 4,
|
||||
'should have generated the command 2 (LINK_TO) with id 1');
|
||||
assert.ok(commands[2][0] === 2 && commands[2][1] === 1,
|
||||
'should have generated the command 2 (DELETE) with id 2');
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
await testUtils.form.clickEdit(form);
|
||||
assert.containsN(form, '.o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3,
|
||||
"should have 3 records in kanban");
|
||||
|
||||
await testUtils.dom.click(form.$('.o_legacy_kanban_view .o_kanban_record:first'));
|
||||
assert.containsOnce($('body'), '.modal.o_modal_full',
|
||||
"there should be a modal opened in full screen");
|
||||
|
||||
// remove button.
|
||||
assert.containsOnce($('.modal').find('.modal-header'), '.o_btn_remove',
|
||||
"there should be a 'Remove' button in the modal header");
|
||||
|
||||
await testUtils.dom.click($('.modal').find('.modal-header .o_btn_remove'));
|
||||
assert.containsNone($('body'), '.modal', "there should be no more modal");
|
||||
assert.containsN(form, '.o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 2,
|
||||
"should have 2 records in kanban");
|
||||
|
||||
// save and check that the correct command has been generated
|
||||
await testUtils.form.clickSave(form);
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
odoo.define('web_enterprise.BasicRenderMobileTests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const BasicRenderer = require('web.BasicRenderer');
|
||||
const FormView = require('web.FormView');
|
||||
const testUtils = require('web.test_utils');
|
||||
|
||||
const createView = testUtils.createView;
|
||||
|
||||
QUnit.module('web_enterprise > basic > basic_render_mobile', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
partner: {
|
||||
fields: {
|
||||
display_name: {string: "Displayed name", type: "char", help: 'The name displayed'},
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 1,
|
||||
display_name: "first record",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}, function () {
|
||||
QUnit.module('Basic Render Mobile');
|
||||
|
||||
QUnit.test(`field tooltip shouldn't remain displayed in mobile`, async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
testUtils.mock.patch(BasicRenderer, {
|
||||
SHOW_AFTER_DELAY: 0,
|
||||
_getTooltipOptions: function () {
|
||||
return Object.assign({}, this._super(...arguments), {
|
||||
animation: false,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="display_name"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
`,
|
||||
});
|
||||
|
||||
const label = form.el.querySelector('label.o_form_label');
|
||||
await testUtils.dom.triggerEvent(label, 'touchstart');
|
||||
assert.strictEqual(
|
||||
document.querySelectorAll('.tooltip .oe_tooltip_string').length,
|
||||
1, "should have a tooltip displayed"
|
||||
);
|
||||
await testUtils.dom.triggerEvent(label, 'touchend');
|
||||
assert.strictEqual(
|
||||
document.querySelectorAll('.tooltip .oe_tooltip_string').length,
|
||||
0, "shouldn't have a tooltip displayed"
|
||||
);
|
||||
form.destroy();
|
||||
|
||||
testUtils.mock.unpatch(BasicRenderer);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
216
web_enterprise/static/tests/legacy/views/kanban_mobile_tests.js
Normal file
216
web_enterprise/static/tests/legacy/views/kanban_mobile_tests.js
Normal file
@@ -0,0 +1,216 @@
|
||||
odoo.define('web_enterprise.kanban_mobile_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const KanbanView = require('web.KanbanView');
|
||||
const { createView, dom} = require('web.test_utils');
|
||||
|
||||
QUnit.module('LegacyViews', {
|
||||
beforeEach() {
|
||||
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: []},
|
||||
{id: 5, bar: false, foo: "Hello \"World\"! #peace_n'_love", int_field: -9, qux: 10, state: "jkl", 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 (legacy) - Mobile")
|
||||
QUnit.test('kanban with searchpanel: rendering in mobile', async function (assert) {
|
||||
assert.expect(34);
|
||||
|
||||
const kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<kanban>
|
||||
<templates><t t-name="kanban-box">
|
||||
<div>
|
||||
<field name="foo"/>
|
||||
</div>
|
||||
</t></templates>
|
||||
</kanban>
|
||||
`,
|
||||
archs: {
|
||||
'partner,false,search': `
|
||||
<search>
|
||||
<searchpanel>
|
||||
<field name="product_id" expand="1" enable_counters="1"/>
|
||||
<field name="state" expand="1" select="multi" enable_counters="1"/>
|
||||
</searchpanel>
|
||||
</search>
|
||||
`,
|
||||
},
|
||||
mockRPC(route, {method}) {
|
||||
if (method && method.includes('search_panel_')) {
|
||||
assert.step(method);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
let $sp = kanban.$(".o_search_panel");
|
||||
|
||||
assert.containsOnce(kanban, ".o_search_panel.o_search_panel_summary");
|
||||
assert.containsNone(document.body, "div.o_search_panel.o_searchview.o_mobile_search");
|
||||
assert.verifySteps([
|
||||
"search_panel_select_range",
|
||||
"search_panel_select_multi_range",
|
||||
]);
|
||||
|
||||
assert.containsOnce($sp, ".fa.fa-filter");
|
||||
assert.containsOnce($sp, ".o_search_panel_current_selection:contains(All)");
|
||||
|
||||
// open the search panel
|
||||
await dom.click($sp);
|
||||
$sp = $(".o_search_panel");
|
||||
|
||||
assert.containsNone(kanban, ".o_search_panel.o_search_panel_summary");
|
||||
assert.containsOnce(document.body, "div.o_search_panel.o_searchview.o_mobile_search");
|
||||
|
||||
assert.containsOnce($sp, ".o_mobile_search_header > button:contains(FILTER)");
|
||||
assert.containsOnce($sp, "button.o_mobile_search_footer:contains(SEE RESULT)");
|
||||
assert.containsN($sp, ".o_search_panel_section", 2);
|
||||
assert.containsOnce($sp, ".o_search_panel_section.o_search_panel_category");
|
||||
assert.containsOnce($sp, ".o_search_panel_section.o_search_panel_filter");
|
||||
assert.containsN($sp, ".o_search_panel_category_value", 3);
|
||||
assert.containsOnce($sp, ".o_search_panel_category_value > header.active", 3);
|
||||
assert.containsN($sp, ".o_search_panel_filter_value", 3);
|
||||
|
||||
// select category
|
||||
await dom.click($sp.find(".o_search_panel_category_value:contains(hello) header"));
|
||||
|
||||
assert.verifySteps([
|
||||
"search_panel_select_range",
|
||||
"search_panel_select_multi_range",
|
||||
]);
|
||||
|
||||
// select filter
|
||||
await dom.click($sp.find(".o_search_panel_filter_value:contains(DEF) input"));
|
||||
|
||||
assert.verifySteps([
|
||||
"search_panel_select_range",
|
||||
"search_panel_select_multi_range",
|
||||
]);
|
||||
|
||||
// close with back button
|
||||
await dom.click($sp.find(".o_mobile_search_header button"));
|
||||
$sp = $(".o_search_panel");
|
||||
|
||||
assert.containsOnce(kanban, ".o_search_panel.o_search_panel_summary");
|
||||
assert.containsNone(document.body, "div.o_search_panel.o_searchview.o_mobile_search");
|
||||
|
||||
// selection is kept when closed
|
||||
|
||||
assert.containsOnce($sp, ".o_search_panel_current_selection");
|
||||
assert.containsOnce($sp, ".o_search_panel_category:contains(hello)");
|
||||
assert.containsOnce($sp, ".o_search_panel_filter:contains(DEF)");
|
||||
|
||||
// open the search panel
|
||||
await dom.click($sp);
|
||||
$sp = $(".o_search_panel");
|
||||
|
||||
assert.containsOnce($sp, ".o_search_panel_category_value > header.active:contains(hello)");
|
||||
assert.containsOnce($sp, ".o_search_panel_filter_value:contains(DEF) input:checked");
|
||||
|
||||
assert.containsNone(kanban, ".o_search_panel.o_search_panel_summary");
|
||||
assert.containsOnce(document.body, "div.o_search_panel.o_searchview.o_mobile_search");
|
||||
|
||||
// close with bottom button
|
||||
await dom.click($sp.find("button.o_mobile_search_footer"));
|
||||
|
||||
assert.containsOnce(kanban, ".o_search_panel.o_search_panel_summary");
|
||||
assert.containsNone(document.body, "div.o_search_panel.o_searchview.o_mobile_search");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
|
||||
QUnit.module('KanbanView Mobile');
|
||||
|
||||
QUnit.test('mobile no quick create column when grouping on non m2o field', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var kanban = await 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>' +
|
||||
'<div><field name="int_field"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['int_field'],
|
||||
});
|
||||
|
||||
assert.containsNone(kanban, '.o_kanban_mobile_add_column', "should not have the add column button");
|
||||
assert.containsNone(kanban.$('.o_column_quick_create'),
|
||||
"should not have column quick create tab as we grouped records on integer field");
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("autofocus quick create form", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: "partner",
|
||||
data: this.data,
|
||||
arch: `<kanban on_create="quick_create">
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div><field name="foo"/></div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
groupBy: ["product_id"],
|
||||
});
|
||||
|
||||
// quick create in first column
|
||||
await dom.click(kanban.$buttons.find(".o-kanban-button-new"));
|
||||
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.strictEqual(document.activeElement, kanban.$(".o_kanban_quick_create .o_input:first")[0],
|
||||
"the first input field should get the focus when the quick_create is opened");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
229
web_enterprise/static/tests/legacy/views/list_mobile_tests.js
Normal file
229
web_enterprise/static/tests/legacy/views/list_mobile_tests.js
Normal file
@@ -0,0 +1,229 @@
|
||||
odoo.define('web_enterprise.list_mobile_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const ListRenderer = require('web.ListRenderer');
|
||||
const ListView = require('web.ListView');
|
||||
const testUtils = require('web.test_utils');
|
||||
|
||||
const { createView, dom, mock } = testUtils;
|
||||
|
||||
QUnit.module("LegacyViews", {
|
||||
beforeEach() {
|
||||
this.data = {
|
||||
foo: {
|
||||
fields: {
|
||||
foo: { string: "Foo", type: "char" },
|
||||
bar: { string: "Bar", type: "boolean" },
|
||||
},
|
||||
records: [
|
||||
{ id: 1, bar: true, foo: "yop" },
|
||||
{ id: 2, bar: true, foo: "blip" },
|
||||
{ id: 3, bar: true, foo: "gnap" },
|
||||
{ id: 4, bar: false, foo: "blip" },
|
||||
],
|
||||
},
|
||||
};
|
||||
mock.patch(ListRenderer, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.LONG_TOUCH_THRESHOLD = 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
afterEach() {
|
||||
mock.unpatch(ListRenderer);
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.module("ListView (legacy) - Mobile");
|
||||
|
||||
QUnit.test("selection is properly displayed (single page)", async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
const list = await createView({
|
||||
touchScreen: true,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
data: this.data,
|
||||
model: 'foo',
|
||||
viewOptions: { hasActionMenus: true },
|
||||
View: ListView,
|
||||
});
|
||||
|
||||
assert.containsN(list, '.o_data_row', 4);
|
||||
assert.containsNone(list, '.o_list_selection_box');
|
||||
|
||||
// select a record
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchend');
|
||||
assert.containsOnce(list, '.o_list_selection_box');
|
||||
assert.containsNone(list.$('.o_list_selection_box'), '.o_list_select_domain');
|
||||
assert.ok(list.$('.o_list_selection_box').text().includes("1 selected"))
|
||||
// unselect a record
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchend');
|
||||
assert.containsNone(list.$('.o_list_selection_box'), '.o_list_select_domain');
|
||||
|
||||
// select 2 records
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchend');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(1)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(1)'), 'touchend');
|
||||
assert.ok(list.$('.o_list_selection_box').text().includes("2 selected"))
|
||||
assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus');
|
||||
await testUtils.controlPanel.toggleActionMenu(list);
|
||||
assert.deepEqual(testUtils.controlPanel.getMenuItemTexts(list), ['Delete'],
|
||||
'action menu should contain the Delete action');
|
||||
// unselect all
|
||||
await dom.click(list.$('.o_discard_selection'));
|
||||
await testUtils.nextTick();
|
||||
assert.containsNone(list, '.o_list_selection_box');
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("selection box is properly displayed (multi pages)", async function (assert) {
|
||||
assert.expect(13);
|
||||
const list = await createView({
|
||||
touchScreen: true,
|
||||
arch: `
|
||||
<tree limit="3">
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
data: this.data,
|
||||
model: 'foo',
|
||||
View: ListView,
|
||||
viewOptions: { hasActionMenus: true },
|
||||
});
|
||||
|
||||
assert.containsN(list, '.o_data_row', 3);
|
||||
assert.containsNone(list, '.o_list_selection_box');
|
||||
|
||||
// select a record
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchend');
|
||||
|
||||
assert.containsOnce(list, '.o_list_selection_box');
|
||||
assert.containsNone(list.$('.o_list_selection_box'), '.o_list_select_domain');
|
||||
assert.strictEqual(list.$('.o_list_selection_box').text().replace(/\s+/g, ' '),
|
||||
" × 1 selected ");
|
||||
assert.containsOnce(list, '.o_list_selection_box');
|
||||
assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus');
|
||||
await testUtils.controlPanel.toggleActionMenu(list);
|
||||
assert.deepEqual(testUtils.controlPanel.getMenuItemTexts(list), ['Delete'],
|
||||
'action menu should contain the Delete action');
|
||||
// select all records of first page
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(1)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(1)'), 'touchend');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(2)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(2)'), 'touchend');
|
||||
assert.containsOnce(list, '.o_list_selection_box');
|
||||
assert.containsOnce(list.$('.o_list_selection_box'), '.o_list_select_domain');
|
||||
assert.strictEqual(list.$('.o_list_selection_box').text().replace(/\s+/g, ' ').trim(),
|
||||
"× 3 selected Select all 4");
|
||||
|
||||
// select all domain
|
||||
await dom.click(list.$('.o_list_selection_box .o_list_select_domain'));
|
||||
|
||||
assert.containsOnce(list, '.o_list_selection_box');
|
||||
assert.strictEqual(list.$('.o_list_selection_box').text().replace(/\s+/g, ' ').trim(),
|
||||
"× All 4 selected");
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("export button is properly hidden", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const list = await createView({
|
||||
touchScreen: true,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
data: this.data,
|
||||
model: 'foo',
|
||||
View: ListView,
|
||||
session: {
|
||||
async user_has_group(group) {
|
||||
if (group === 'base.group_allow_export') {
|
||||
return true;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(list, '.o_data_row', 4);
|
||||
assert.isNotVisible(list.$buttons.find('.o_list_export_xlsx'));
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('editable readonly list view is disabled', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const list = await createView({
|
||||
touchScreen: true,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
</tree>`,
|
||||
data: this.data,
|
||||
model: 'foo',
|
||||
View: ListView,
|
||||
});
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchstart');
|
||||
await dom.triggerEvent(list.$('.o_data_row:eq(0)'), 'touchend');
|
||||
await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)'));
|
||||
assert.containsNone(list, '.o_selected_row .o_field_widget[name=foo]',
|
||||
"The listview should not contains an edit field");
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("add custom field button not shown in mobile (with opt. col.)", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const list = await testUtils.createView({
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar" optional="hide"/>
|
||||
</tree>`,
|
||||
data: this.data,
|
||||
model: 'foo',
|
||||
touchScreen: true,
|
||||
View: ListView,
|
||||
});
|
||||
assert.containsOnce(list.$('table'), '.o_optional_columns_dropdown_toggle');
|
||||
await testUtils.dom.click(list.$('table .o_optional_columns_dropdown_toggle'));
|
||||
const $dropdown = list.$('div.o_optional_columns');
|
||||
assert.containsOnce($dropdown, 'div.dropdown-item');
|
||||
assert.containsNone($dropdown, 'button.dropdown-item-studio');
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("add custom field button not shown to non-system users (wo opt. col.)", async function (assert) {
|
||||
assert.expect(1);
|
||||
const list = await testUtils.createView({
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
data: this.data,
|
||||
model: 'foo',
|
||||
touchScreen: true,
|
||||
View: ListView,
|
||||
});
|
||||
assert.containsNone(list.$('table'), '.o_optional_columns_dropdown_toggle');
|
||||
list.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
259
web_enterprise/static/tests/legacy/views/list_tests.js
Normal file
259
web_enterprise/static/tests/legacy/views/list_tests.js
Normal file
@@ -0,0 +1,259 @@
|
||||
odoo.define('web_enterprise.list_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
const AbstractStorageService = require('web.AbstractStorageService');
|
||||
const ListRenderer = require('web.ListRenderer');
|
||||
const ListView = require('web.ListView');
|
||||
const RamStorage = require('web.RamStorage');
|
||||
const testUtils = require('web.test_utils');
|
||||
const { unblockUI } = require('web.framework');
|
||||
const { patch, unpatch, UnknownPatchError } = require('web.utils');
|
||||
const PromoteStudioDialog = require('web_enterprise.PromoteStudioDialog');
|
||||
|
||||
QUnit.module('web_enterprise', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
foo: {
|
||||
fields: {
|
||||
foo: {string: "Foo", type: "char"},
|
||||
bar: {string: "Bar", type: "boolean"},
|
||||
},
|
||||
records: [
|
||||
{id: 1, bar: true, foo: "yop"},
|
||||
{id: 2, bar: true, foo: "blip"},
|
||||
{id: 3, bar: true, foo: "gnap"},
|
||||
{id: 4, bar: false, foo: "blip"},
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this.RamStorageService = AbstractStorageService.extend({
|
||||
storage: new RamStorage(),
|
||||
});
|
||||
}
|
||||
}, function () {
|
||||
|
||||
QUnit.module('ListView (Legacy)');
|
||||
|
||||
QUnit.test("add custom field button with other optional columns - studio not installed", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
let listPatch;
|
||||
try {
|
||||
listPatch = unpatch(ListRenderer.prototype, 'web_studio.ListRenderer');
|
||||
} catch (e) {
|
||||
if (!(e instanceof UnknownPatchError)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
testUtils.mock.patch(PromoteStudioDialog, {
|
||||
_reloadPage: function () {
|
||||
assert.step('window_reload');
|
||||
unblockUI(); // the UI is normally unblocked by the reload
|
||||
}
|
||||
});
|
||||
|
||||
const list = await testUtils.createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar" optional="hide"/>
|
||||
</tree>`,
|
||||
session: {
|
||||
is_system: true
|
||||
},
|
||||
action: {
|
||||
xml_id: "action_43",
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'search_read' && args.model === 'ir.module.module') {
|
||||
assert.step('studio_module_id');
|
||||
return Promise.resolve([{id: 42}]);
|
||||
}
|
||||
if (args.method === 'button_immediate_install' && args.model === 'ir.module.module') {
|
||||
assert.deepEqual(args.args[0], [42], "Should be the id of studio module returned by the search read");
|
||||
assert.step('studio_module_install');
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
services: {
|
||||
local_storage: this.RamStorageService,
|
||||
},
|
||||
});
|
||||
|
||||
assert.ok(list.$('.o_data_row').length > 0);
|
||||
assert.containsOnce(list.$('table'), '.o_optional_columns_dropdown_toggle');
|
||||
await testUtils.dom.click(list.$('table .o_optional_columns_dropdown_toggle'));
|
||||
const $dropdown = list.$('div.o_optional_columns');
|
||||
assert.containsOnce($dropdown, 'div.dropdown-item');
|
||||
assert.containsOnce($dropdown, 'button.dropdown-item-studio');
|
||||
|
||||
await testUtils.dom.click(list.$('div.o_optional_columns button.dropdown-item-studio'));
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(document.body, '.modal-studio');
|
||||
await testUtils.dom.click($('.modal-studio .o_install_studio'));
|
||||
assert.equal(window.localStorage.openStudioOnReload, 'main');
|
||||
assert.verifySteps(['studio_module_id', 'studio_module_install', 'window_reload']);
|
||||
// wash localStorage
|
||||
window.localStorage.openStudioOnReload = false;
|
||||
|
||||
testUtils.mock.unpatch(PromoteStudioDialog);
|
||||
if (listPatch) {
|
||||
patch(ListRenderer.prototype, 'web_studio.ListRenderer', listPatch);
|
||||
}
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("add custom field button without other optional columns - studio not installed", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
let listPatch;
|
||||
try {
|
||||
listPatch = unpatch(ListRenderer.prototype, 'web_studio.ListRenderer');
|
||||
} catch (e) {
|
||||
if (!(e instanceof UnknownPatchError)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
testUtils.mock.patch(PromoteStudioDialog, {
|
||||
_reloadPage: function () {
|
||||
assert.step('window_reload');
|
||||
unblockUI(); // the UI is normally unblocked by the reload
|
||||
}
|
||||
});
|
||||
|
||||
const list = await testUtils.createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
session: {
|
||||
is_system: true
|
||||
},
|
||||
action: {
|
||||
xml_id: "action_43",
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'search_read' && args.model === 'ir.module.module') {
|
||||
assert.step('studio_module_id');
|
||||
return Promise.resolve([{id: 42}]);
|
||||
}
|
||||
if (args.method === 'button_immediate_install' && args.model === 'ir.module.module') {
|
||||
assert.deepEqual(args.args[0], [42], "Should be the id of studio module returned by the search read");
|
||||
assert.step('studio_module_install');
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
services: {
|
||||
local_storage: this.RamStorageService,
|
||||
},
|
||||
});
|
||||
|
||||
assert.ok(list.$('.o_data_row').length > 0);
|
||||
assert.containsOnce(list.$('table'), '.o_optional_columns_dropdown_toggle');
|
||||
await testUtils.dom.click(list.$('table .o_optional_columns_dropdown_toggle'));
|
||||
const $dropdown = list.$('div.o_optional_columns');
|
||||
assert.containsNone($dropdown, 'div.dropdown-item');
|
||||
assert.containsOnce($dropdown, 'button.dropdown-item-studio');
|
||||
|
||||
await testUtils.dom.click(list.$('div.o_optional_columns button.dropdown-item-studio'));
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(document.body, '.modal-studio');
|
||||
await testUtils.dom.click($('.modal-studio .o_install_studio'));
|
||||
assert.equal(window.localStorage.openStudioOnReload, 'main');
|
||||
assert.verifySteps(['studio_module_id', 'studio_module_install', 'window_reload']);
|
||||
// wash localStorage
|
||||
window.localStorage.openStudioOnReload = false;
|
||||
|
||||
if (listPatch) {
|
||||
patch(ListRenderer.prototype, 'web_studio.ListRenderer', listPatch);
|
||||
}
|
||||
testUtils.mock.unpatch(PromoteStudioDialog);
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("add custom field button not shown to non-system users (with opt. col.)", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const list = await testUtils.createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar" optional="hide"/>
|
||||
</tree>`,
|
||||
session: {
|
||||
is_system: false
|
||||
},
|
||||
action: {
|
||||
xml_id: "action_43",
|
||||
},
|
||||
});
|
||||
assert.containsOnce(list.$('table'), '.o_optional_columns_dropdown_toggle');
|
||||
await testUtils.dom.click(list.$('table .o_optional_columns_dropdown_toggle'));
|
||||
const $dropdown = list.$('div.o_optional_columns');
|
||||
assert.containsOnce($dropdown, 'div.dropdown-item');
|
||||
assert.containsNone($dropdown, 'button.dropdown-item-studio');
|
||||
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("add custom field button not shown to non-system users (wo opt. col.)", async function (assert) {
|
||||
assert.expect(1);
|
||||
const list = await testUtils.createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
session: {
|
||||
is_system: false
|
||||
},
|
||||
action: {
|
||||
xml_id: "action_43",
|
||||
},
|
||||
});
|
||||
assert.containsNone(list.$('table'), '.o_optional_columns_dropdown_toggle');
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("add custom field button not shown with invalid action", async function (assert) {
|
||||
assert.expect(1);
|
||||
const list = await testUtils.createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
session: {
|
||||
is_system: true
|
||||
},
|
||||
action: {
|
||||
xml_id: null,
|
||||
},
|
||||
});
|
||||
assert.containsNone(list.$('div.o_optional_columns'), 'button.dropdown-item-studio');
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
84
web_enterprise/static/tests/mobile/burger_menu_tests.js
Normal file
84
web_enterprise/static/tests/mobile/burger_menu_tests.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/** @odoo-module **/
|
||||
import { click, legacyExtraNextTick } from "@web/../tests/helpers/utils";
|
||||
import { getActionManagerServerData } from "@web/../tests/webclient/helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createEnterpriseWebClient } from "@web_enterprise/../tests/helpers";
|
||||
import { EnterpriseBurgerMenu } from "@web_enterprise/webclient/burger_menu/burger_menu";
|
||||
import { homeMenuService } from "@web_enterprise/webclient/home_menu/home_menu_service";
|
||||
import { companyService } from "@web/webclient/company_service";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
||||
|
||||
/**
|
||||
* Note: The asserts are all based on document.body (instead of getFixture() by example) because
|
||||
* the burger menu is porteled into the dom and is not part of the qunit fixture.
|
||||
*/
|
||||
|
||||
let serverData;
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
QUnit.module("Burger Menu Enterprise", {
|
||||
beforeEach() {
|
||||
serverData = getActionManagerServerData();
|
||||
|
||||
serviceRegistry.add("enterprise_subscription", enterpriseSubscriptionService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("company", companyService);
|
||||
serviceRegistry.add("home_menu", homeMenuService);
|
||||
|
||||
registry.category("systray").add("burger_menu", {
|
||||
Component: EnterpriseBurgerMenu,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test("Burger Menu on home menu", async (assert) => {
|
||||
assert.expect(5);
|
||||
|
||||
await createEnterpriseWebClient({ serverData });
|
||||
assert.containsNone(document.body, ".o_burger_menu");
|
||||
assert.isVisible(document.body.querySelector(".o_home_menu"));
|
||||
|
||||
await click(document.body, ".o_mobile_menu_toggle");
|
||||
assert.containsOnce(document.body, ".o_burger_menu");
|
||||
assert.containsOnce(document.body, ".o_user_menu_mobile");
|
||||
await click(document.body, ".o_burger_menu_close");
|
||||
assert.containsNone(document.body, ".o_burger_menu");
|
||||
});
|
||||
|
||||
QUnit.test("Burger Menu on home menu over an App", async (assert) => {
|
||||
assert.expect(5);
|
||||
|
||||
serverData.menus[1].children = [99];
|
||||
serverData.menus[99] = {
|
||||
id: 99,
|
||||
children: [],
|
||||
name: "SubMenu",
|
||||
appID: 1,
|
||||
actionID: 1002,
|
||||
xmlid: "",
|
||||
webIconData: undefined,
|
||||
webIcon: false,
|
||||
};
|
||||
|
||||
await createEnterpriseWebClient({ serverData });
|
||||
await click(document.body, ".o_app:first-of-type");
|
||||
await legacyExtraNextTick();
|
||||
await click(document.body, ".o_menu_toggle");
|
||||
await legacyExtraNextTick();
|
||||
|
||||
assert.containsNone(document.body, ".o_burger_menu");
|
||||
assert.isVisible(document.body.querySelector(".o_home_menu"));
|
||||
|
||||
await click(document.body, ".o_mobile_menu_toggle");
|
||||
assert.containsOnce(document.body, ".o_burger_menu");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_burger_menu nav.o_burger_menu_content li"
|
||||
);
|
||||
assert.doesNotHaveClass(
|
||||
document.body.querySelector(".o_burger_menu_content"),
|
||||
"o_burger_menu_dark"
|
||||
);
|
||||
});
|
||||
185
web_enterprise/static/tests/mobile/pivot_view_tests.js
Normal file
185
web_enterprise/static/tests/mobile/pivot_view_tests.js
Normal file
@@ -0,0 +1,185 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { dialogService } from "@web/core/dialog/dialog_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import {
|
||||
makeFakeLocalizationService,
|
||||
makeFakeUserService,
|
||||
} from "@web/../tests/helpers/mock_services";
|
||||
import { getFixture, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import {
|
||||
setupControlPanelFavoriteMenuRegistry,
|
||||
setupControlPanelServiceRegistry,
|
||||
} from "@web/../tests/search/helpers";
|
||||
import { makeView } from "@web/../tests/views/helpers";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
QUnit.module("Views", (hooks) => {
|
||||
hooks.beforeEach(() => {
|
||||
target = getFixture();
|
||||
serverData = {
|
||||
models: {
|
||||
partner: {
|
||||
fields: {
|
||||
foo: {
|
||||
string: "Foo",
|
||||
type: "integer",
|
||||
searchable: true,
|
||||
group_operator: "sum",
|
||||
},
|
||||
bar: { string: "bar", type: "boolean", store: true, sortable: true },
|
||||
date: { string: "Date", type: "date", store: true, sortable: true },
|
||||
product_id: {
|
||||
string: "Product",
|
||||
type: "many2one",
|
||||
relation: "product",
|
||||
store: true,
|
||||
sortable: true,
|
||||
},
|
||||
other_product_id: {
|
||||
string: "Other Product",
|
||||
type: "many2one",
|
||||
relation: "product",
|
||||
store: true,
|
||||
sortable: true,
|
||||
},
|
||||
non_stored_m2o: {
|
||||
string: "Non Stored M2O",
|
||||
type: "many2one",
|
||||
relation: "product",
|
||||
},
|
||||
customer: {
|
||||
string: "Customer",
|
||||
type: "many2one",
|
||||
relation: "customer",
|
||||
store: true,
|
||||
sortable: true,
|
||||
},
|
||||
computed_field: {
|
||||
string: "Computed and not stored",
|
||||
type: "integer",
|
||||
compute: true,
|
||||
group_operator: "sum",
|
||||
},
|
||||
company_type: {
|
||||
string: "Company Type",
|
||||
type: "selection",
|
||||
selection: [
|
||||
["company", "Company"],
|
||||
["individual", "individual"],
|
||||
],
|
||||
searchable: true,
|
||||
sortable: true,
|
||||
store: true,
|
||||
},
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 1,
|
||||
foo: 12,
|
||||
bar: true,
|
||||
date: "2016-12-14",
|
||||
product_id: 37,
|
||||
customer: 1,
|
||||
computed_field: 19,
|
||||
company_type: "company",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
foo: 1,
|
||||
bar: true,
|
||||
date: "2016-10-26",
|
||||
product_id: 41,
|
||||
customer: 2,
|
||||
computed_field: 23,
|
||||
company_type: "individual",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
foo: 17,
|
||||
bar: true,
|
||||
date: "2016-12-15",
|
||||
product_id: 41,
|
||||
customer: 2,
|
||||
computed_field: 26,
|
||||
company_type: "company",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
foo: 2,
|
||||
bar: false,
|
||||
date: "2016-04-11",
|
||||
product_id: 41,
|
||||
customer: 1,
|
||||
computed_field: 19,
|
||||
company_type: "individual",
|
||||
},
|
||||
],
|
||||
},
|
||||
product: {
|
||||
fields: {
|
||||
name: { string: "Product Name", type: "char" },
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 37,
|
||||
display_name: "xphone",
|
||||
},
|
||||
{
|
||||
id: 41,
|
||||
display_name: "xpad",
|
||||
},
|
||||
],
|
||||
},
|
||||
customer: {
|
||||
fields: {
|
||||
name: { string: "Customer Name", type: "char" },
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 1,
|
||||
display_name: "First",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
display_name: "Second",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
setupControlPanelFavoriteMenuRegistry();
|
||||
setupControlPanelServiceRegistry();
|
||||
serviceRegistry.add("dialog", dialogService);
|
||||
serviceRegistry.add("localization", makeFakeLocalizationService());
|
||||
serviceRegistry.add("user", makeFakeUserService());
|
||||
patchWithCleanup(browser, { setTimeout: (fn) => fn() });
|
||||
});
|
||||
|
||||
QUnit.module("PivotView");
|
||||
|
||||
QUnit.test("simple pivot rendering", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
await makeView({
|
||||
type: "pivot",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: `
|
||||
<pivot string="Partners">
|
||||
<field name="foo" type="measure"/>
|
||||
</pivot>`,
|
||||
});
|
||||
|
||||
assert.hasClass(target.querySelector(".o_pivot_view"), "o_view_controller");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
"td.o_pivot_cell_value:contains(32)",
|
||||
"should contain a pivot cell with the sum of all records"
|
||||
);
|
||||
});
|
||||
});
|
||||
53
web_enterprise/static/tests/mobile/webclient_mobile_tests.js
Normal file
53
web_enterprise/static/tests/mobile/webclient_mobile_tests.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { getActionManagerServerData, doAction } from "@web/../tests/webclient/helpers";
|
||||
import { homeMenuService } from "@web_enterprise/webclient/home_menu/home_menu_service";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createEnterpriseWebClient } from "../helpers";
|
||||
import { click, getFixture } from "@web/../tests/helpers/utils";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
QUnit.module("WebClient Mobile", (hooks) => {
|
||||
let serverData;
|
||||
hooks.beforeEach(() => {
|
||||
serverData = getActionManagerServerData();
|
||||
serviceRegistry.add("home_menu", homeMenuService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("enterprise_subscription", enterpriseSubscriptionService);
|
||||
});
|
||||
|
||||
QUnit.test("scroll position is kept", async (assert) => {
|
||||
// This test relies on the fact that the scrollable element in mobile
|
||||
// is view's root node.
|
||||
const record = serverData.models.partner.records[0];
|
||||
serverData.models.partner.records = [];
|
||||
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const rec = Object.assign({}, record);
|
||||
rec.id = i + 1;
|
||||
rec.display_name = `Record ${rec.id}`;
|
||||
serverData.models.partner.records.push(rec);
|
||||
}
|
||||
|
||||
// force the html node to be scrollable element
|
||||
const target = getFixture();
|
||||
const webClient = await createEnterpriseWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, 3); // partners in list/kanban
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
|
||||
target.querySelector(".o_kanban_view").scrollTo(0, 123);
|
||||
await click(target.querySelectorAll(".o_kanban_record")[20]);
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsNone(target, ".o_kanban_view");
|
||||
|
||||
await click(target.querySelector(".o_control_panel .o_back_button"));
|
||||
assert.containsNone(target, ".o_form_view");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
|
||||
assert.strictEqual(target.querySelector(".o_kanban_view").scrollTop, 123);
|
||||
});
|
||||
});
|
||||
6
web_enterprise/static/tests/views/disable_patch.js
Normal file
6
web_enterprise/static/tests/views/disable_patch.js
Normal file
@@ -0,0 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { unpatch } from "@web/core/utils/patch";
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
|
||||
unpatch(ListRenderer.prototype, "web_enterprise.ListRendererDesktop");
|
||||
247
web_enterprise/static/tests/views/list_tests.js
Normal file
247
web_enterprise/static/tests/views/list_tests.js
Normal file
@@ -0,0 +1,247 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { getFixture, patchWithCleanup, click, nextTick } from "@web/../tests/helpers/utils";
|
||||
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
|
||||
import { patch, unpatch } from "@web/core/utils/patch";
|
||||
import { session } from "@web/session";
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { patchListRendererDesktop } from "@web_enterprise/views/list/list_renderer_desktop";
|
||||
|
||||
let config;
|
||||
let serverData;
|
||||
let target;
|
||||
QUnit.module(
|
||||
"web_enterprise",
|
||||
{
|
||||
beforeEach() {
|
||||
target = getFixture();
|
||||
config = {
|
||||
actionId: 1,
|
||||
actionType: "ir.actions.act_window",
|
||||
};
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
foo: { string: "Foo", type: "char" },
|
||||
bar: { string: "Bar", type: "boolean" },
|
||||
},
|
||||
records: [
|
||||
{ id: 1, bar: true, foo: "yop" },
|
||||
{ id: 2, bar: true, foo: "blip" },
|
||||
{ id: 3, bar: true, foo: "gnap" },
|
||||
{ id: 4, bar: false, foo: "blip" },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
patch(
|
||||
ListRenderer.prototype,
|
||||
"web_enterprise.ListRendererDesktop",
|
||||
patchListRendererDesktop
|
||||
);
|
||||
|
||||
setupViewRegistries();
|
||||
},
|
||||
afterEach() {
|
||||
unpatch(ListRenderer.prototype, "web_enterprise.ListRendererDesktop");
|
||||
},
|
||||
},
|
||||
function () {
|
||||
QUnit.module("ListView");
|
||||
|
||||
QUnit.test(
|
||||
"add custom field button with other optional columns - studio not installed",
|
||||
async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
patchWithCleanup(session, { is_system: true });
|
||||
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "list",
|
||||
resModel: "foo",
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar" optional="hide"/>
|
||||
</tree>`,
|
||||
mockRPC(route, args) {
|
||||
if (args.method === "search_read" && args.model === "ir.module.module") {
|
||||
assert.step("studio_module_id");
|
||||
return Promise.resolve([{ id: 42 }]);
|
||||
}
|
||||
if (
|
||||
args.method === "button_immediate_install" &&
|
||||
args.model === "ir.module.module"
|
||||
) {
|
||||
assert.deepEqual(
|
||||
args.args[0],
|
||||
[42],
|
||||
"Should be the id of studio module returned by the search read"
|
||||
);
|
||||
assert.step("studio_module_install");
|
||||
return true;
|
||||
}
|
||||
},
|
||||
config,
|
||||
});
|
||||
|
||||
patchWithCleanup(browser.location, {
|
||||
reload: function () {
|
||||
assert.step("window_reload");
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(target, ".o_data_row", 4);
|
||||
assert.containsOnce(target, ".o_optional_columns_dropdown_toggle");
|
||||
|
||||
await click(target, ".o_optional_columns_dropdown_toggle");
|
||||
const dropdown = target.querySelector(".o_optional_columns_dropdown");
|
||||
assert.containsN(dropdown, ".dropdown-item", 2);
|
||||
assert.containsOnce(dropdown, ".dropdown-item-studio");
|
||||
|
||||
await click(target, ".o_optional_columns_dropdown .dropdown-item-studio");
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".modal-studio");
|
||||
|
||||
await click(target, ".modal .o_install_studio");
|
||||
assert.equal(browser.localStorage.getItem("openStudioOnReload"), "main");
|
||||
assert.verifySteps(["studio_module_id", "studio_module_install", "window_reload"]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"add custom field button without other optional columns - studio not installed",
|
||||
async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
patchWithCleanup(session, { is_system: true });
|
||||
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "list",
|
||||
resModel: "foo",
|
||||
config,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "search_read" && args.model === "ir.module.module") {
|
||||
assert.step("studio_module_id");
|
||||
return Promise.resolve([{ id: 42 }]);
|
||||
}
|
||||
if (
|
||||
args.method === "button_immediate_install" &&
|
||||
args.model === "ir.module.module"
|
||||
) {
|
||||
assert.deepEqual(
|
||||
args.args[0],
|
||||
[42],
|
||||
"Should be the id of studio module returned by the search read"
|
||||
);
|
||||
assert.step("studio_module_install");
|
||||
return true;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
patchWithCleanup(browser.location, {
|
||||
reload: function () {
|
||||
assert.step("window_reload");
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(target, ".o_data_row", 4);
|
||||
assert.containsOnce(target, ".o_optional_columns_dropdown_toggle");
|
||||
|
||||
await click(target, ".o_optional_columns_dropdown_toggle");
|
||||
const dropdown = target.querySelector(".o_optional_columns_dropdown");
|
||||
assert.containsOnce(dropdown, ".dropdown-item");
|
||||
assert.containsOnce(dropdown, ".dropdown-item-studio");
|
||||
|
||||
await click(target, ".o_optional_columns_dropdown .dropdown-item-studio");
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".modal-studio");
|
||||
|
||||
await click(target, ".modal .o_install_studio");
|
||||
assert.equal(browser.localStorage.getItem("openStudioOnReload"), "main");
|
||||
assert.verifySteps(["studio_module_id", "studio_module_install", "window_reload"]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"add custom field button not shown to non-system users (with opt. col.)",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
patchWithCleanup(session, { is_system: false });
|
||||
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "list",
|
||||
resModel: "foo",
|
||||
config,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar" optional="hide"/>
|
||||
</tree>`,
|
||||
});
|
||||
|
||||
assert.containsOnce(target, ".o_optional_columns_dropdown_toggle");
|
||||
await click(target, ".o_optional_columns_dropdown_toggle");
|
||||
const dropdown = target.querySelector(".o_optional_columns_dropdown");
|
||||
assert.containsOnce(dropdown, ".dropdown-item");
|
||||
assert.containsNone(dropdown, ".dropdown-item-studio");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"add custom field button not shown to non-system users (wo opt. col.)",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
patchWithCleanup(session, { is_system: false });
|
||||
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "list",
|
||||
resModel: "foo",
|
||||
config,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
});
|
||||
|
||||
assert.containsNone(target, ".o_optional_columns_dropdown_toggle");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"add custom field button not shown with invalid action",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
patchWithCleanup(session, { is_system: false });
|
||||
config.actionId = null;
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "list",
|
||||
resModel: "foo",
|
||||
config,
|
||||
arch: `
|
||||
<tree>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
</tree>`,
|
||||
});
|
||||
|
||||
assert.containsNone(target, ".o_optional_columns_dropdown_toggle");
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
784
web_enterprise/static/tests/webclient/expiration_panel_tests.js
Normal file
784
web_enterprise/static/tests/webclient/expiration_panel_tests.js
Normal file
@@ -0,0 +1,784 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
|
||||
import { click, getFixture, patchDate, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { makeFakeNotificationService } from "@web/../tests/helpers/mock_services";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import testUtils from "web.test_utils";
|
||||
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
||||
import { homeMenuService } from "@web_enterprise/webclient/home_menu/home_menu_service";
|
||||
import { session } from "@web/session";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
let target;
|
||||
|
||||
async function createExpirationPanel(params = {}) {
|
||||
const mockedCookieService = {
|
||||
name: "cookie",
|
||||
start() {
|
||||
return Object.assign(
|
||||
{
|
||||
current: {},
|
||||
setCookie() {},
|
||||
deleteCookie() {},
|
||||
},
|
||||
params.cookie
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
serviceRegistry.add(mockedCookieService.name, mockedCookieService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("home_menu", homeMenuService);
|
||||
serviceRegistry.add(
|
||||
"notification",
|
||||
makeFakeNotificationService(params.mockNotification || (() => {}))
|
||||
);
|
||||
patchWithCleanup(session, { ...params.session });
|
||||
serviceRegistry.add("enterprise_subscription", enterpriseSubscriptionService);
|
||||
patchWithCleanup(browser, params.browser);
|
||||
|
||||
const webclient = await createWebClient({
|
||||
mockRPC: params.mockRPC,
|
||||
});
|
||||
await doAction(webclient, "menu");
|
||||
return webclient;
|
||||
}
|
||||
|
||||
QUnit.module("web_enterprise", function ({ beforeEach }) {
|
||||
beforeEach(() => {
|
||||
target = getFixture();
|
||||
});
|
||||
|
||||
QUnit.module("Expiration Panel");
|
||||
|
||||
QUnit.test("Expiration Panel one app installed", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-11-09 12:00:00",
|
||||
expiration_reason: "",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database will expire in 1 month."
|
||||
);
|
||||
|
||||
// Color should be grey
|
||||
assert.hasClass(target.querySelector(".database_expiration_panel"), "alert-info");
|
||||
|
||||
// Close the expiration panel
|
||||
await click(target.querySelector(".oe_instance_hide_panel"));
|
||||
|
||||
assert.containsNone(target, ".database_expiration_panel");
|
||||
});
|
||||
|
||||
QUnit.test("Expiration Panel one app installed, buy subscription", async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-24 12:00:00",
|
||||
expiration_reason: "demo",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route) {
|
||||
if (route === "/web/dataset/call_kw/res.users/search_count") {
|
||||
return 7;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This demo database will expire in 14 days. Register your subscription or buy a subscription."
|
||||
);
|
||||
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-warning",
|
||||
"Color should be orange"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_show",
|
||||
"Part 'Register your subscription'"
|
||||
);
|
||||
assert.containsOnce(target, ".oe_instance_buy", "Part 'buy a subscription'");
|
||||
assert.containsNone(
|
||||
target,
|
||||
".oe_instance_register_form",
|
||||
"There should be no registration form"
|
||||
);
|
||||
|
||||
// Click on 'buy subscription'
|
||||
await click(target.querySelector(".oe_instance_buy"));
|
||||
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"Expiration Panel one app installed, try several times to register subscription",
|
||||
async function (assert) {
|
||||
assert.expect(44);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
let callToGetParamCount = 0;
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-15 12:00:00",
|
||||
expiration_reason: "trial",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockNotification(message, options) {
|
||||
assert.step(JSON.stringify({ message, options }));
|
||||
},
|
||||
mockRPC(route, { args }) {
|
||||
if (route === "/web/dataset/call_kw/ir.config_parameter/get_param") {
|
||||
assert.step("get_param");
|
||||
if (args[0] === "database.already_linked_subscription_url") {
|
||||
return false;
|
||||
}
|
||||
if (args[0] === "database.already_linked_email") {
|
||||
return "super_company_admin@gmail.com";
|
||||
}
|
||||
assert.strictEqual(args[0], "database.expiration_date");
|
||||
callToGetParamCount++;
|
||||
if (callToGetParamCount <= 3) {
|
||||
return "2019-10-15 12:00:00";
|
||||
} else {
|
||||
return "2019-11-15 12:00:00";
|
||||
}
|
||||
}
|
||||
if (route === "/web/dataset/call_kw/ir.config_parameter/set_param") {
|
||||
assert.step("set_param");
|
||||
assert.strictEqual(args[0], "database.enterprise_code");
|
||||
if (callToGetParamCount === 1) {
|
||||
assert.strictEqual(args[1], "ABCDEF");
|
||||
} else {
|
||||
assert.strictEqual(args[1], "ABC");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
route ===
|
||||
"/web/dataset/call_kw/publisher_warranty.contract/update_notification"
|
||||
) {
|
||||
assert.step("update_notification");
|
||||
assert.ok(args[0] instanceof Array && args[0].length === 0);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database will expire in 5 days. Register your subscription or buy a subscription."
|
||||
);
|
||||
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-danger",
|
||||
"Color should be red"
|
||||
);
|
||||
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_show",
|
||||
"Part 'Register your subscription'"
|
||||
);
|
||||
assert.containsOnce(target, ".oe_instance_buy", "Part 'buy a subscription'");
|
||||
assert.containsNone(
|
||||
target,
|
||||
".oe_instance_register_form",
|
||||
"There should be no registration form"
|
||||
);
|
||||
|
||||
// Click on 'register your subscription'
|
||||
await click(target.querySelector(".oe_instance_register_show"));
|
||||
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_form",
|
||||
"there should be a registration form"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
'.oe_instance_register_form input[placeholder="Paste code here"]',
|
||||
"with an input with place holder 'Paste code here'"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_form button",
|
||||
"and a button 'REGISTER'"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register_form button").innerText,
|
||||
"REGISTER"
|
||||
);
|
||||
|
||||
await click(target.querySelector(".oe_instance_register_form button"));
|
||||
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_form",
|
||||
"there should be a registration form"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
'.oe_instance_register_form input[placeholder="Paste code here"]',
|
||||
"with an input with place holder 'Paste code here'"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_form button",
|
||||
"and a button 'REGISTER'"
|
||||
);
|
||||
|
||||
await testUtils.fields.editInput(
|
||||
target.querySelector(".oe_instance_register_form input"),
|
||||
"ABCDEF"
|
||||
);
|
||||
await click(target.querySelector(".oe_instance_register_form button"));
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"Something went wrong while registering your database. You can try again or contact Odoo Support."
|
||||
);
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-danger",
|
||||
"Color should be red"
|
||||
);
|
||||
assert.containsOnce(target, "span.oe_instance_error");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_form",
|
||||
"there should be a registration form"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
'.oe_instance_register_form input[placeholder="Paste code here"]',
|
||||
"with an input with place holder 'Paste code here'"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_form button",
|
||||
"and a button 'REGISTER'"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register_form button").innerText,
|
||||
"RETRY"
|
||||
);
|
||||
|
||||
await testUtils.fields.editInput(
|
||||
target.querySelector(".oe_instance_register_form input"),
|
||||
"ABC"
|
||||
);
|
||||
await click(target.querySelector(".oe_instance_register_form button"));
|
||||
|
||||
assert.containsNone(
|
||||
target,
|
||||
".database_expiration_panel",
|
||||
"expiration panel should be gone"
|
||||
);
|
||||
|
||||
assert.verifySteps([
|
||||
// second try to submit
|
||||
"get_param",
|
||||
"set_param",
|
||||
"get_param",
|
||||
"get_param",
|
||||
"update_notification",
|
||||
"get_param",
|
||||
// third try
|
||||
"get_param",
|
||||
"set_param",
|
||||
"get_param",
|
||||
"get_param",
|
||||
"update_notification",
|
||||
"get_param",
|
||||
`{"message":"Thank you, your registration was successful! Your database is valid until November 15, 2019.","options":{"type":"success"}}`,
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"Expiration Panel one app installed, subscription already linked",
|
||||
async function (assert) {
|
||||
assert.expect(12);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
// There are some line breaks mismatches between local and runbot test instances.
|
||||
// Since they don't affect the layout and we're only interested in the text itself,
|
||||
// we normalize whitespaces and line breaks from both the expected and end result
|
||||
const formatWhiteSpaces = (text) =>
|
||||
text
|
||||
.split(/[\n\s]/)
|
||||
.filter((w) => w !== "")
|
||||
.join(" ");
|
||||
|
||||
let getExpirationDateCount = 0;
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-15 12:00:00",
|
||||
expiration_reason: "trial",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { method, args }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (route === "/already/linked/send/mail/url") {
|
||||
return {
|
||||
result: false,
|
||||
reason: "By design",
|
||||
};
|
||||
}
|
||||
assert.step(method);
|
||||
if (args[0] === "database.expiration_date") {
|
||||
getExpirationDateCount++;
|
||||
if (getExpirationDateCount === 1) {
|
||||
return "2019-10-15 12:00:00";
|
||||
} else {
|
||||
return "2019-11-17 12:00:00";
|
||||
}
|
||||
}
|
||||
if (args[0] === "database.already_linked_subscription_url") {
|
||||
return "www.super_company.com";
|
||||
}
|
||||
if (args[0] === "database.already_linked_send_mail_url") {
|
||||
return "/already/linked/send/mail/url";
|
||||
}
|
||||
if (args[0] === "database.already_linked_email") {
|
||||
return "super_company_admin@gmail.com";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database will expire in 5 days. Register your subscription or buy a subscription."
|
||||
);
|
||||
|
||||
// Click on 'register your subscription'
|
||||
await click(target.querySelector(".oe_instance_register_show"));
|
||||
await testUtils.fields.editInput(
|
||||
target.querySelector(".oe_instance_register_form input"),
|
||||
"ABC"
|
||||
);
|
||||
await click(target.querySelector(".oe_instance_register_form button"));
|
||||
|
||||
assert.strictEqual(
|
||||
formatWhiteSpaces(
|
||||
target.querySelector(".oe_instance_register.oe_database_already_linked")
|
||||
.innerText
|
||||
),
|
||||
formatWhiteSpaces(
|
||||
`Your subscription is already linked to a database.
|
||||
To unlink it you can either:
|
||||
Login to your Odoo.com dashboard then unlink your previous database: www.super_company.com
|
||||
Click here to send an email to the subscription owner (email: super_company_admin@gmail.com) with the instructions to follow`
|
||||
)
|
||||
);
|
||||
|
||||
await click(target.querySelector("a.oe_contract_send_mail"));
|
||||
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-danger",
|
||||
"Color should be red"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
formatWhiteSpaces(
|
||||
target.querySelector(".oe_instance_register.oe_database_already_linked")
|
||||
.innerText
|
||||
),
|
||||
formatWhiteSpaces(
|
||||
`Your subscription is already linked to a database.
|
||||
To unlink it you can either:
|
||||
Login to your Odoo.com dashboard then unlink your previous database: www.super_company.com
|
||||
Click here to send an email to the subscription owner (email: super_company_admin@gmail.com) with the instructions to follow
|
||||
Unable to send the instructions by email, please contact the Odoo Support
|
||||
Error reason: By design`
|
||||
)
|
||||
);
|
||||
|
||||
assert.verifySteps([
|
||||
"get_param",
|
||||
"set_param",
|
||||
"get_param",
|
||||
"get_param",
|
||||
"update_notification",
|
||||
"get_param",
|
||||
"get_param",
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("One app installed, database expired", async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
let callToGetParamCount = 0;
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-08 12:00:00",
|
||||
expiration_reason: "trial",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { args, method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
if (args[0] === "database.already_linked_subscription_url") {
|
||||
return false;
|
||||
}
|
||||
callToGetParamCount++;
|
||||
if (callToGetParamCount === 1) {
|
||||
return "2019-10-09 12:00:00";
|
||||
} else {
|
||||
return "2019-11-09 12:00:00";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database has expired. Register your subscription or buy a subscription."
|
||||
);
|
||||
assert.containsOnce(target, ".o_blockUI", "UI should be blocked");
|
||||
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-danger",
|
||||
"Color should be red"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".oe_instance_register_show",
|
||||
"Part 'Register your subscription'"
|
||||
);
|
||||
assert.containsOnce(target, ".oe_instance_buy", "Part 'buy a subscription'");
|
||||
|
||||
assert.containsNone(target, ".oe_instance_register_form");
|
||||
|
||||
// Click on 'Register your subscription'
|
||||
await click(target.querySelector(".oe_instance_register_show"));
|
||||
await testUtils.fields.editInput(
|
||||
target.querySelector(".oe_instance_register_form input"),
|
||||
"ABC"
|
||||
);
|
||||
await click(target.querySelector(".oe_instance_register_form button"));
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"Thank you, your registration was successful! Your database is valid until November 9, 2019."
|
||||
);
|
||||
assert.containsNone(target, ".o_blockUI", "UI should no longer be blocked");
|
||||
});
|
||||
|
||||
QUnit.test("One app installed, renew", async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-20 12:00:00",
|
||||
expiration_reason: "renewal",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { args, method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
assert.step("get_param");
|
||||
assert.strictEqual(args[0], "database.enterprise_code");
|
||||
return "ABC";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database will expire in 10 days. Renew your subscription "
|
||||
);
|
||||
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-warning",
|
||||
"Color should be red"
|
||||
);
|
||||
assert.containsOnce(target, ".oe_instance_renew", "Part 'Register your subscription'");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
"a.check_enterprise_status",
|
||||
"there should be a button for status checking"
|
||||
);
|
||||
|
||||
assert.containsNone(target, ".oe_instance_register_form");
|
||||
|
||||
// Click on 'Renew your subscription'
|
||||
await click(target.querySelector(".oe_instance_renew"));
|
||||
|
||||
assert.verifySteps(["get_param"]);
|
||||
});
|
||||
|
||||
QUnit.test("One app installed, check status and get success", async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-20 12:00:00",
|
||||
expiration_reason: "renewal",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { args, method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
assert.step("get_param");
|
||||
assert.strictEqual(args[0], "database.expiration_date");
|
||||
return "2019-10-24 12:00:00";
|
||||
}
|
||||
if (method === "update_notification") {
|
||||
assert.step("update_notification");
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
// click on "Refresh subscription status"
|
||||
const refreshButton = target.querySelector("a.check_enterprise_status");
|
||||
assert.strictEqual(refreshButton.getAttribute("aria-label"), "Refresh subscription status");
|
||||
await click(refreshButton);
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register.oe_subscription_updated").innerText,
|
||||
"Your subscription was updated and is valid until October 24, 2019."
|
||||
);
|
||||
|
||||
assert.verifySteps(["update_notification", "get_param"]);
|
||||
});
|
||||
|
||||
// Why would we want to reload the page when we check the status and it hasn't changed?
|
||||
QUnit.skip("One app installed, check status and get page reload", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-20 12:00:00",
|
||||
expiration_reason: "renewal",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
assert.step("get_param");
|
||||
return "2019-10-20 12:00:00";
|
||||
}
|
||||
if (method === "update_notification") {
|
||||
assert.step("update_notification");
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
// click on "Refresh subscription status"
|
||||
await click(target.querySelector("a.check_enterprise_status"));
|
||||
|
||||
assert.verifySteps(["update_notification", "get_param", "reloadPage"]);
|
||||
});
|
||||
|
||||
QUnit.test("One app installed, upgrade database", async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-20 12:00:00",
|
||||
expiration_reason: "upsell",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { args, method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
assert.step("get_param");
|
||||
assert.strictEqual(args[0], "database.enterprise_code");
|
||||
return "ABC";
|
||||
}
|
||||
if (method === "search_count") {
|
||||
assert.step("search_count");
|
||||
return 13;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database will expire in 10 days. You have more users or more apps installed than your subscription allows.\n" +
|
||||
"Upgrade your subscription "
|
||||
);
|
||||
|
||||
// click on "Upgrade your subscription"
|
||||
await click(target.querySelector("a.oe_instance_upsell"));
|
||||
|
||||
assert.verifySteps(["get_param", "search_count"]);
|
||||
});
|
||||
|
||||
QUnit.test("One app installed, message for non admin user", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
patchDate(2019, 9, 10, 12, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-11-08 12:00:00",
|
||||
expiration_reason: "",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "user",
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database will expire in 29 days. Log in as an administrator to correct the issue."
|
||||
);
|
||||
|
||||
assert.hasClass(
|
||||
target.querySelector(".database_expiration_panel"),
|
||||
"alert-info",
|
||||
"Color should be grey"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("One app installed, navigation to renewal page", async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
patchDate(2019, 11, 10, 0, 0, 0);
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-20 12:00:00",
|
||||
expiration_reason: "renewal",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
mockRPC(route, { args, method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
assert.step("get_param");
|
||||
assert.strictEqual(args[0], "database.enterprise_code");
|
||||
return "ABC";
|
||||
}
|
||||
if (method === "update_notification") {
|
||||
assert.step("update_notification");
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"This database has expired. Renew your subscription "
|
||||
);
|
||||
|
||||
assert.hasClass(target.querySelector(".database_expiration_panel"), "alert-danger");
|
||||
assert.containsOnce(target, ".oe_instance_renew", "Part 'Register your subscription'");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
"a.check_enterprise_status",
|
||||
"there should be a button for status checking"
|
||||
);
|
||||
|
||||
assert.containsNone(target, ".oe_instance_register_form");
|
||||
|
||||
// Click on 'Renew your subscription'
|
||||
await click(target.querySelector(".oe_instance_renew"));
|
||||
|
||||
|
||||
|
||||
assert.verifySteps(["get_param"]);
|
||||
});
|
||||
|
||||
QUnit.test("One app installed, different locale (arabic)", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
patchDate(2019, 9, 25, 12, 0, 0);
|
||||
patchWithCleanup(luxon.Settings, {
|
||||
defaultLocale: "ar-001",
|
||||
defaultNumberingSystem: "arab",
|
||||
});
|
||||
|
||||
await createExpirationPanel({
|
||||
session: {
|
||||
expiration_date: "2019-10-20 12:00:00",
|
||||
expiration_reason: "renewal",
|
||||
notification_type: true, // used by subscription service to know whether mail is installed
|
||||
warning: "admin",
|
||||
},
|
||||
async mockRPC(route, { method }) {
|
||||
if (route === "/web/webclient/load_menus") {
|
||||
return;
|
||||
}
|
||||
if (method === "get_param") {
|
||||
return "2019-11-09 12:00:00";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
await click(target, ".check_enterprise_status");
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".oe_instance_register").innerText,
|
||||
"Your subscription was updated and is valid until ٩ نوفمبر ٢٠١٩."
|
||||
);
|
||||
});
|
||||
});
|
||||
370
web_enterprise/static/tests/webclient/home_menu_tests.js
Normal file
370
web_enterprise/static/tests/webclient/home_menu_tests.js
Normal file
@@ -0,0 +1,370 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services";
|
||||
import { getFixture, nextTick, triggerHotkey, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { commandService } from "@web/core/commands/command_service";
|
||||
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { HomeMenu } from "@web_enterprise/webclient/home_menu/home_menu";
|
||||
import testUtils from "web.test_utils";
|
||||
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
||||
import { session } from "@web/session";
|
||||
import { templates } from "@web/core/assets";
|
||||
|
||||
|
||||
const { App, EventBus } = owl;
|
||||
const patchDate = testUtils.mock.patchDate;
|
||||
const serviceRegistry = registry.category("services");
|
||||
let target;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
async function createHomeMenu(homeMenuProps) {
|
||||
const env = await makeTestEnv();
|
||||
const app = new App(HomeMenu, {
|
||||
env,
|
||||
props: homeMenuProps,
|
||||
templates,
|
||||
test: true,
|
||||
});
|
||||
const homeMenu = await app.mount(target);
|
||||
registerCleanup(() => app.destroy());
|
||||
return homeMenu;
|
||||
}
|
||||
|
||||
async function walkOn(assert, path) {
|
||||
for (const step of path) {
|
||||
triggerHotkey(`${step.shiftKey ? "shift+" : ""}${step.key}`);
|
||||
await nextTick();
|
||||
assert.hasClass(
|
||||
target.querySelectorAll(".o_menuitem")[step.index],
|
||||
"o_focused",
|
||||
`step ${step.number}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
let homeMenuProps;
|
||||
let bus;
|
||||
QUnit.module(
|
||||
"web_enterprise",
|
||||
{
|
||||
beforeEach: function () {
|
||||
homeMenuProps = {
|
||||
apps: [
|
||||
{
|
||||
actionID: 121,
|
||||
appID: 1,
|
||||
id: 1,
|
||||
label: "Discuss",
|
||||
parents: "",
|
||||
webIcon: false,
|
||||
xmlid: "app.1",
|
||||
},
|
||||
{
|
||||
actionID: 122,
|
||||
appID: 2,
|
||||
id: 2,
|
||||
label: "Calendar",
|
||||
parents: "",
|
||||
webIcon: false,
|
||||
xmlid: "app.2",
|
||||
},
|
||||
{
|
||||
actionID: 123,
|
||||
appID: 3,
|
||||
id: 3,
|
||||
label: "Contacts",
|
||||
parents: "",
|
||||
webIcon: false,
|
||||
xmlid: "app.3",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
bus = new EventBus();
|
||||
const fakeHomeMenuService = {
|
||||
name: "home_menu",
|
||||
start() {
|
||||
return {
|
||||
toggle(show) {
|
||||
bus.trigger("toggle", show);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
const fakeMenuService = {
|
||||
name: "menu",
|
||||
start() {
|
||||
return {
|
||||
selectMenu(menu) {
|
||||
bus.trigger("selectMenu", menu.id);
|
||||
},
|
||||
getMenu() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
serviceRegistry.add("ui", uiService);
|
||||
serviceRegistry.add("hotkey", hotkeyService);
|
||||
serviceRegistry.add("command", commandService);
|
||||
serviceRegistry.add("localization", makeFakeLocalizationService());
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add(enterpriseSubscriptionService.name, enterpriseSubscriptionService);
|
||||
serviceRegistry.add(fakeHomeMenuService.name, fakeHomeMenuService);
|
||||
serviceRegistry.add(fakeMenuService.name, fakeMenuService);
|
||||
|
||||
target = getFixture();
|
||||
},
|
||||
},
|
||||
function () {
|
||||
QUnit.module("HomeMenu");
|
||||
|
||||
QUnit.test("ESC Support", async function (assert) {
|
||||
bus.on("toggle", null, (show) => {
|
||||
assert.step(`toggle ${show}`);
|
||||
});
|
||||
await createHomeMenu(homeMenuProps);
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "Escape" });
|
||||
assert.verifySteps(["toggle false"]);
|
||||
});
|
||||
|
||||
QUnit.test("Click on an app", async function (assert) {
|
||||
bus.on("selectMenu", null, (menuId) => {
|
||||
assert.step(`selectMenu ${menuId}`);
|
||||
});
|
||||
await createHomeMenu(homeMenuProps);
|
||||
|
||||
await testUtils.dom.click(target.querySelectorAll(".o_menuitem")[0]);
|
||||
assert.verifySteps(["selectMenu 1"]);
|
||||
});
|
||||
|
||||
QUnit.test("Display Expiration Panel (no module installed)", async function (assert) {
|
||||
const unpatchDate = patchDate(2019, 9, 10, 0, 0, 0);
|
||||
registerCleanup(unpatchDate);
|
||||
|
||||
patchWithCleanup(session, {
|
||||
expiration_date: "2019-11-01 12:00:00",
|
||||
expiration_reason: "",
|
||||
isMailInstalled: false,
|
||||
warning: "admin",
|
||||
});
|
||||
let cookie = false;
|
||||
const mockedCookieService = {
|
||||
name: "cookie",
|
||||
start() {
|
||||
return {
|
||||
get current() {
|
||||
return cookie;
|
||||
},
|
||||
setCookie() {
|
||||
cookie = true;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
serviceRegistry.add(mockedCookieService.name, mockedCookieService);
|
||||
|
||||
await createHomeMenu(homeMenuProps);
|
||||
|
||||
assert.containsOnce(target, ".database_expiration_panel");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".database_expiration_panel .oe_instance_register").innerText,
|
||||
"You will be able to register your database once you have installed your first app.",
|
||||
"There should be an expiration panel displayed"
|
||||
);
|
||||
|
||||
// Close the expiration panel
|
||||
await testUtils.dom.click(
|
||||
target.querySelector(".database_expiration_panel .oe_instance_hide_panel")
|
||||
);
|
||||
assert.containsNone(target, ".database_expiration_panel");
|
||||
});
|
||||
|
||||
QUnit.test("Navigation (only apps, only one line)", async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
homeMenuProps = {
|
||||
apps: new Array(3).fill().map((x, i) => {
|
||||
return {
|
||||
actionID: 120 + i,
|
||||
appID: i + 1,
|
||||
id: i + 1,
|
||||
label: `0${i}`,
|
||||
parents: "",
|
||||
webIcon: false,
|
||||
xmlid: `app.${i}`,
|
||||
};
|
||||
}),
|
||||
};
|
||||
await createHomeMenu(homeMenuProps);
|
||||
|
||||
const path = [
|
||||
{ number: 0, key: "ArrowDown", index: 0 },
|
||||
{ number: 1, key: "ArrowRight", index: 1 },
|
||||
{ number: 2, key: "Tab", index: 2 },
|
||||
{ number: 3, key: "ArrowRight", index: 0 },
|
||||
{ number: 4, key: "Tab", shiftKey: true, index: 2 },
|
||||
{ number: 5, key: "ArrowLeft", index: 1 },
|
||||
{ number: 6, key: "ArrowDown", index: 1 },
|
||||
{ number: 7, key: "ArrowUp", index: 1 },
|
||||
];
|
||||
|
||||
await walkOn(assert, path);
|
||||
});
|
||||
|
||||
QUnit.test("Navigation (only apps, two lines, one incomplete)", async function (assert) {
|
||||
assert.expect(19);
|
||||
|
||||
homeMenuProps = {
|
||||
apps: new Array(8).fill().map((x, i) => {
|
||||
return {
|
||||
actionID: 121,
|
||||
appID: i + 1,
|
||||
id: i + 1,
|
||||
label: `0${i}`,
|
||||
parents: "",
|
||||
webIcon: false,
|
||||
xmlid: `app.${i}`,
|
||||
};
|
||||
}),
|
||||
};
|
||||
await createHomeMenu(homeMenuProps);
|
||||
|
||||
const path = [
|
||||
{ number: 1, key: "ArrowRight", index: 0 },
|
||||
{ number: 2, key: "ArrowUp", index: 6 },
|
||||
{ number: 3, key: "ArrowUp", index: 0 },
|
||||
{ number: 4, key: "ArrowDown", index: 6 },
|
||||
{ number: 5, key: "ArrowDown", index: 0 },
|
||||
{ number: 6, key: "ArrowRight", index: 1 },
|
||||
{ number: 7, key: "ArrowRight", index: 2 },
|
||||
{ number: 8, key: "ArrowUp", index: 7 },
|
||||
{ number: 9, key: "ArrowUp", index: 1 },
|
||||
{ number: 10, key: "ArrowRight", index: 2 },
|
||||
{ number: 11, key: "ArrowDown", index: 7 },
|
||||
{ number: 12, key: "ArrowDown", index: 1 },
|
||||
{ number: 13, key: "ArrowUp", index: 7 },
|
||||
{ number: 14, key: "ArrowRight", index: 6 },
|
||||
{ number: 15, key: "ArrowLeft", index: 7 },
|
||||
{ number: 16, key: "ArrowUp", index: 1 },
|
||||
{ number: 17, key: "ArrowLeft", index: 0 },
|
||||
{ number: 18, key: "ArrowLeft", index: 5 },
|
||||
{ number: 19, key: "ArrowRight", index: 0 },
|
||||
];
|
||||
|
||||
await walkOn(assert, path);
|
||||
});
|
||||
|
||||
QUnit.test("Navigation and open an app in the home menu", async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
bus.on("selectMenu", null, (menuId) => {
|
||||
assert.step(`selectMenu ${menuId}`);
|
||||
});
|
||||
await createHomeMenu(homeMenuProps);
|
||||
|
||||
// No app selected so nothing to open
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "Enter" });
|
||||
assert.verifySteps([]);
|
||||
|
||||
const path = [
|
||||
{ number: 0, key: "ArrowDown", index: 0 },
|
||||
{ number: 1, key: "ArrowRight", index: 1 },
|
||||
{ number: 2, key: "Tab", index: 2 },
|
||||
{ number: 3, key: "shift+Tab", index: 1 },
|
||||
];
|
||||
|
||||
await walkOn(assert, path);
|
||||
|
||||
// open first app (Calendar)
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "Enter" });
|
||||
|
||||
assert.verifySteps(["selectMenu 2"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"The HomeMenu input takes the focus when you press a key only if no other element is the activeElement",
|
||||
async function (assert) {
|
||||
const target = getFixture();
|
||||
const homeMenu = await createHomeMenu(homeMenuProps);
|
||||
const input = target.querySelector(".o_search_hidden");
|
||||
assert.strictEqual(document.activeElement, input);
|
||||
|
||||
const activeElement = document.createElement("div");
|
||||
homeMenu.env.services.ui.activateElement(activeElement);
|
||||
// remove the focus from the input
|
||||
const otherInput = document.createElement("input");
|
||||
target.querySelector(".o_home_menu").appendChild(otherInput);
|
||||
otherInput.focus();
|
||||
otherInput.blur();
|
||||
assert.notEqual(document.activeElement, input);
|
||||
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
||||
await nextTick();
|
||||
assert.notEqual(document.activeElement, input);
|
||||
|
||||
homeMenu.env.services.ui.deactivateElement(activeElement);
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
||||
await nextTick();
|
||||
assert.strictEqual(document.activeElement, input);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"The HomeMenu input does not take the focus if it is already on another input",
|
||||
async function (assert) {
|
||||
const target = getFixture();
|
||||
await createHomeMenu(homeMenuProps);
|
||||
const homeMenuInput = target.querySelector(".o_search_hidden");
|
||||
assert.strictEqual(document.activeElement, homeMenuInput);
|
||||
|
||||
const otherInput = document.createElement("input");
|
||||
target.querySelector(".o_home_menu").appendChild(otherInput);
|
||||
otherInput.focus();
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
||||
await nextTick();
|
||||
assert.notEqual(document.activeElement, homeMenuInput);
|
||||
|
||||
otherInput.remove();
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
||||
await nextTick();
|
||||
assert.strictEqual(document.activeElement, homeMenuInput);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"The HomeMenu input does not take the focus if it is already on a textarea",
|
||||
async function (assert) {
|
||||
const target = getFixture();
|
||||
await createHomeMenu(homeMenuProps);
|
||||
const homeMenuInput = target.querySelector(".o_search_hidden");
|
||||
assert.strictEqual(document.activeElement, homeMenuInput);
|
||||
|
||||
const textarea = document.createElement("textarea");
|
||||
target.querySelector(".o_home_menu").appendChild(textarea);
|
||||
textarea.focus();
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
||||
await nextTick();
|
||||
assert.notEqual(document.activeElement, homeMenuInput);
|
||||
|
||||
textarea.remove();
|
||||
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
||||
await nextTick();
|
||||
assert.strictEqual(document.activeElement, homeMenuInput);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,528 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
click,
|
||||
editInput,
|
||||
getFixture,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { doAction, getActionManagerServerData, loadState } from "@web/../tests/webclient/helpers";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { editView } from "@web/views/debug_items";
|
||||
import { createEnterpriseWebClient } from "@web_enterprise/../tests/helpers";
|
||||
import { homeMenuService } from "@web_enterprise/webclient/home_menu/home_menu_service";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
||||
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
|
||||
const { Component, xml } = owl;
|
||||
|
||||
let serverData;
|
||||
let fixture;
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
// Should test ONLY the webClient and features present in Enterprise
|
||||
// Those tests rely on hidden view to be in CSS: display: none
|
||||
QUnit.module("WebClient Enterprise", (hooks) => {
|
||||
hooks.beforeEach(() => {
|
||||
serverData = getActionManagerServerData();
|
||||
fixture = getFixture();
|
||||
serviceRegistry.add("home_menu", homeMenuService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("enterprise_subscription", enterpriseSubscriptionService);
|
||||
});
|
||||
|
||||
QUnit.module("basic flow with home menu", (hooks) => {
|
||||
let mockRPC;
|
||||
hooks.beforeEach((assert) => {
|
||||
serverData.menus[1].actionID = 4;
|
||||
serverData.menus.root.children = [1];
|
||||
serverData.views["partner,false,form"] = `
|
||||
<form>
|
||||
<field name="display_name"/>
|
||||
<field name="m2o" open_target="current"/>
|
||||
</form>`;
|
||||
mockRPC = async (route) => {
|
||||
assert.step(route);
|
||||
if (route === "/web/dataset/call_kw/partner/get_formview_action") {
|
||||
return {
|
||||
type: "ir.actions.act_window",
|
||||
res_model: "partner",
|
||||
view_type: "form",
|
||||
view_mode: "form",
|
||||
views: [[false, "form"]],
|
||||
target: "current",
|
||||
res_id: 2,
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.test("1 -- start up", async function (assert) {
|
||||
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
assert.ok(document.body.classList.contains("o_home_menu_background"));
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
assert.isNotVisible(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.containsOnce(fixture, ".o_app.o_menuitem");
|
||||
});
|
||||
|
||||
QUnit.test("2 -- navbar updates on displaying an action", async function (assert) {
|
||||
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
await click(fixture.querySelector(".o_app.o_menuitem"));
|
||||
assert.verifySteps([
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/web_search_read",
|
||||
]);
|
||||
assert.notOk(document.body.classList.contains("o_home_menu_background"));
|
||||
assert.containsNone(fixture, ".o_home_menu");
|
||||
assert.containsOnce(fixture, ".o_kanban_view");
|
||||
const menuToggle = fixture.querySelector(".o_menu_toggle");
|
||||
assert.isVisible(menuToggle);
|
||||
assert.notOk(menuToggle.classList.contains("o_menu_toggle_back"));
|
||||
});
|
||||
|
||||
QUnit.test("3 -- push another action in the breadcrumb", async function (assert) {
|
||||
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
await click(fixture.querySelector(".o_app.o_menuitem"));
|
||||
assert.verifySteps([
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/web_search_read",
|
||||
]);
|
||||
await click(fixture.querySelector(".o_kanban_record"));
|
||||
await nextTick(); // there is another tick to update navbar and destroy HomeMenu
|
||||
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
||||
assert.isVisible(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.containsOnce(fixture, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
fixture.querySelector(".breadcrumb-item.active").textContent,
|
||||
"First record"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("4 -- push a third action in the breadcrumb", async function (assert) {
|
||||
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
await click(fixture.querySelector(".o_app.o_menuitem"));
|
||||
assert.verifySteps([
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/web_search_read",
|
||||
]);
|
||||
await click(fixture.querySelector(".o_kanban_record"));
|
||||
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
||||
await click(fixture, '.o_field_widget[name="m2o"] .o_external_button', true);
|
||||
assert.verifySteps([
|
||||
"/web/dataset/call_kw/partner/get_formview_action",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
]);
|
||||
assert.containsOnce(fixture, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
fixture.querySelector(".breadcrumb-item.active").textContent,
|
||||
"Second record"
|
||||
);
|
||||
assert.containsN(fixture, ".breadcrumb-item", 3);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"5 -- switch to HomeMenu from an action with 2 breadcrumbs",
|
||||
async function (assert) {
|
||||
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
await click(fixture.querySelector(".o_app.o_menuitem"));
|
||||
assert.verifySteps([
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/web_search_read",
|
||||
]);
|
||||
await click(fixture.querySelector(".o_kanban_record"));
|
||||
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
||||
await click(fixture, '.o_field_widget[name="m2o"] .o_external_button', true);
|
||||
assert.verifySteps([
|
||||
"/web/dataset/call_kw/partner/get_formview_action",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
]);
|
||||
const menuToggle = fixture.querySelector(".o_menu_toggle");
|
||||
await click(menuToggle);
|
||||
assert.verifySteps([]);
|
||||
assert.ok(menuToggle.classList.contains("o_menu_toggle_back"));
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
assert.isNotVisible(fixture.querySelector(".o_form_view"));
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("6 -- back to underlying action with many breadcrumbs", async function (assert) {
|
||||
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
await click(fixture.querySelector(".o_app.o_menuitem"));
|
||||
assert.verifySteps([
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/web_search_read",
|
||||
]);
|
||||
await click(fixture.querySelector(".o_kanban_record"));
|
||||
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
||||
await click(fixture, '.o_field_widget[name="m2o"] .o_external_button', true);
|
||||
assert.verifySteps([
|
||||
"/web/dataset/call_kw/partner/get_formview_action",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
]);
|
||||
const menuToggle = fixture.querySelector(".o_menu_toggle");
|
||||
await click(menuToggle);
|
||||
await click(menuToggle);
|
||||
// if we don't reload on going back to underlying action
|
||||
// assert.verifySteps(
|
||||
// [],
|
||||
// "the underlying view should not reload when toggling the HomeMenu to off"
|
||||
// );
|
||||
// endif
|
||||
// if we reload on going back to underlying action
|
||||
assert.verifySteps(
|
||||
["/web/dataset/call_kw/partner/read"],
|
||||
"the underlying view should reload when toggling the HomeMenu to off"
|
||||
);
|
||||
// endif
|
||||
assert.containsNone(fixture, ".o_home_menu");
|
||||
assert.containsOnce(fixture, ".o_form_view");
|
||||
assert.notOk(menuToggle.classList.contains("o_menu_toggle_back"));
|
||||
assert.strictEqual(
|
||||
fixture.querySelector(".breadcrumb-item.active").textContent,
|
||||
"Second record"
|
||||
);
|
||||
assert.containsN(fixture, ".breadcrumb-item", 3);
|
||||
});
|
||||
|
||||
QUnit.test("restore the newly created record in form view (legacy)", async (assert) => {
|
||||
const action = serverData.actions[6];
|
||||
delete action.res_id;
|
||||
action.target = "current";
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
|
||||
await doAction(webClient, 6);
|
||||
assert.containsOnce(fixture, ".o_form_view");
|
||||
assert.containsOnce(fixture, ".o_form_view .o_form_editable");
|
||||
await editInput(fixture, ".o_field_widget[name=display_name] input", "red right hand");
|
||||
await click(fixture.querySelector(".o_form_button_save"));
|
||||
assert.strictEqual(
|
||||
fixture.querySelector(".breadcrumb-item.active").textContent,
|
||||
"red right hand"
|
||||
);
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.isNotVisible(fixture.querySelector(".o_form_view"));
|
||||
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.containsOnce(fixture, ".o_form_view");
|
||||
assert.containsOnce(fixture, ".o_form_view .o_form_saved");
|
||||
assert.strictEqual(
|
||||
fixture.querySelector(".breadcrumb-item.active").textContent,
|
||||
"red right hand"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.skip("fast clicking on restore (implementation detail)", async (assert) => {
|
||||
assert.expect(6);
|
||||
|
||||
let doVeryFastClick = false;
|
||||
|
||||
class DelayedClientAction extends Component {
|
||||
setup() {
|
||||
owl.onMounted(() => {
|
||||
if (doVeryFastClick) {
|
||||
doVeryFastClick = false;
|
||||
click(fixture.querySelector(".o_menu_toggle"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
DelayedClientAction.template = xml`<div class='delayed_client_action'>
|
||||
<button t-on-click="resolve">RESOLVE</button>
|
||||
</div>`;
|
||||
|
||||
registry.category("actions").add("DelayedClientAction", DelayedClientAction);
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
await doAction(webClient, "DelayedClientAction");
|
||||
await nextTick();
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.isVisible(fixture.querySelector(".o_home_menu"));
|
||||
assert.isNotVisible(fixture.querySelector(".delayed_client_action"));
|
||||
|
||||
doVeryFastClick = true;
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
await nextTick();
|
||||
// off homemenu
|
||||
assert.isVisible(fixture.querySelector(".o_home_menu"));
|
||||
assert.isNotVisible(fixture.querySelector(".delayed_client_action"));
|
||||
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
await nextTick();
|
||||
assert.containsNone(fixture, ".o_home_menu");
|
||||
assert.containsOnce(fixture, ".delayed_client_action");
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("clear unCommittedChanges when toggling home menu", async function (assert) {
|
||||
assert.expect(6);
|
||||
// Edit a form view, don't save, toggle home menu
|
||||
// the autosave feature of the Form view is activated
|
||||
// and relied upon by this test
|
||||
|
||||
const mockRPC = (route, args) => {
|
||||
if (args.method === "create") {
|
||||
assert.strictEqual(args.model, "partner");
|
||||
assert.deepEqual(args.args, [
|
||||
{
|
||||
display_name: "red right hand",
|
||||
foo: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
await doAction(webClient, 3, { viewType: "form" });
|
||||
assert.containsOnce(fixture, ".o_form_view .o_form_editable");
|
||||
await editInput(fixture, ".o_field_widget[name=display_name] input", "red right hand");
|
||||
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.containsNone(fixture, ".o_form_view");
|
||||
assert.containsNone(fixture, ".modal");
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
});
|
||||
|
||||
QUnit.test("can have HomeMenu and dialog action", async function (assert) {
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
assert.containsNone(fixture, ".modal .o_form_view");
|
||||
await doAction(webClient, 5);
|
||||
assert.containsOnce(fixture, ".modal .o_form_view");
|
||||
assert.isVisible(fixture.querySelector(".modal .o_form_view"));
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
});
|
||||
|
||||
QUnit.test("supports attachments of apps deleted", async function (assert) {
|
||||
// When doing a pg_restore without the filestore
|
||||
// LPE fixme: may not be necessary anymore since menus are not HomeMenu props anymore
|
||||
serverData.menus = {
|
||||
root: { id: "root", children: [1], name: "root", appID: "root" },
|
||||
1: {
|
||||
id: 1,
|
||||
appID: 1,
|
||||
actionID: 1,
|
||||
xmlid: "",
|
||||
name: "Partners",
|
||||
children: [],
|
||||
webIconData: "",
|
||||
webIcon: "bloop,bloop",
|
||||
},
|
||||
};
|
||||
patchWithCleanup(odoo, { debug: "1" });
|
||||
await createEnterpriseWebClient({ fixture, serverData });
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"debug manager resets to global items when home menu is displayed",
|
||||
async function (assert) {
|
||||
const debugRegistry = registry.category("debug");
|
||||
debugRegistry.category("view").add("editView", editView);
|
||||
debugRegistry.category("default").add("item_1", () => {
|
||||
return {
|
||||
type: "item",
|
||||
description: "globalItem",
|
||||
callback: () => {},
|
||||
sequence: 10,
|
||||
};
|
||||
});
|
||||
const mockRPC = async (route) => {
|
||||
if (route.includes("check_access_rights")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
patchWithCleanup(odoo, { debug: "1" });
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
||||
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
||||
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
||||
assert.containsNone(
|
||||
fixture,
|
||||
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
||||
);
|
||||
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
||||
await doAction(webClient, 1);
|
||||
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
||||
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
||||
assert.containsOnce(
|
||||
fixture,
|
||||
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
||||
);
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
||||
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
||||
assert.containsNone(
|
||||
fixture,
|
||||
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
||||
);
|
||||
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
||||
await doAction(webClient, 3);
|
||||
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
||||
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
||||
assert.containsOnce(
|
||||
fixture,
|
||||
".o_debug_manager .dropdown-item:contains('Edit View: List')"
|
||||
);
|
||||
assert.containsNone(
|
||||
fixture,
|
||||
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"url state is well handled when going in and out of the HomeMenu",
|
||||
async function (assert) {
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, { action: "menu" });
|
||||
|
||||
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(2)"));
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: 1002,
|
||||
menu_id: 2,
|
||||
});
|
||||
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, { action: "menu" });
|
||||
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
await nextTick();
|
||||
// if we reload on going back to underlying action
|
||||
// end if
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: 1002,
|
||||
menu_id: 2,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"underlying action's menu items are invisible when HomeMenu is displayed",
|
||||
async function (assert) {
|
||||
serverData.menus[1].children = [99];
|
||||
serverData.menus[99] = {
|
||||
id: 99,
|
||||
children: [],
|
||||
name: "SubMenu",
|
||||
appID: 1,
|
||||
actionID: 1002,
|
||||
xmlid: "",
|
||||
webIconData: undefined,
|
||||
webIcon: false,
|
||||
};
|
||||
await createEnterpriseWebClient({ fixture, serverData });
|
||||
assert.containsNone(fixture, "nav .o_menu_sections");
|
||||
assert.containsNone(fixture, "nav .o_menu_brand");
|
||||
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(1)"));
|
||||
await nextTick();
|
||||
assert.containsOnce(fixture, "nav .o_menu_sections");
|
||||
assert.containsOnce(fixture, "nav .o_menu_brand");
|
||||
assert.isVisible(fixture.querySelector(".o_menu_sections"));
|
||||
assert.isVisible(fixture.querySelector(".o_menu_brand"));
|
||||
await click(fixture.querySelector(".o_menu_toggle"));
|
||||
assert.containsOnce(fixture, "nav .o_menu_sections");
|
||||
assert.containsOnce(fixture, "nav .o_menu_brand");
|
||||
assert.isNotVisible(fixture.querySelector(".o_menu_sections"));
|
||||
assert.isNotVisible(fixture.querySelector(".o_menu_brand"));
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("loadState back and forth keeps relevant keys in state", async function (assert) {
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
|
||||
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(2)"));
|
||||
await nextTick();
|
||||
assert.containsOnce(fixture, ".test_client_action");
|
||||
assert.containsNone(fixture, ".o_home_menu");
|
||||
const state = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(state, {
|
||||
action: 1002,
|
||||
menu_id: 2,
|
||||
});
|
||||
|
||||
await loadState(webClient, {});
|
||||
assert.containsNone(fixture, ".test_client_action");
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: "menu",
|
||||
});
|
||||
|
||||
await loadState(webClient, state);
|
||||
assert.containsOnce(fixture, ".test_client_action");
|
||||
assert.containsNone(fixture, ".o_home_menu");
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, state);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"go back to home menu using browser back button (i.e. loadState)",
|
||||
async function (assert) {
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
assert.isNotVisible(fixture.querySelector(".o_main_navbar .o_menu_toggle"));
|
||||
|
||||
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(2)"));
|
||||
assert.containsOnce(fixture, ".test_client_action");
|
||||
assert.containsNone(fixture, ".o_home_menu");
|
||||
|
||||
await loadState(webClient, { action: "menu" }); // FIXME: this might need to be changed
|
||||
assert.containsNone(fixture, ".test_client_action");
|
||||
assert.containsOnce(fixture, ".o_home_menu");
|
||||
assert.isNotVisible(fixture.querySelector(".o_main_navbar .o_menu_toggle"));
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("initial action crashes", async (assert) => {
|
||||
const handler = (ev) => {
|
||||
// need to preventDefault to remove error from console (so python test pass)
|
||||
ev.preventDefault();
|
||||
};
|
||||
window.addEventListener("unhandledrejection", handler);
|
||||
registerCleanup(() => window.removeEventListener("unhandledrejection", handler));
|
||||
|
||||
patchWithCleanup(QUnit, {
|
||||
onUnhandledRejection: () => {},
|
||||
});
|
||||
|
||||
browser.location.hash = "#action=__test__client__action__&menu_id=1";
|
||||
const ClientAction = registry.category("actions").get("__test__client__action__");
|
||||
class Override extends ClientAction {
|
||||
setup() {
|
||||
super.setup();
|
||||
assert.step("clientAction setup");
|
||||
throw new Error("my error");
|
||||
}
|
||||
}
|
||||
registry.category("actions").add("__test__client__action__", Override, { force: true });
|
||||
|
||||
registry.category("services").add("error", errorService);
|
||||
|
||||
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
||||
assert.verifySteps(["clientAction setup"]);
|
||||
assert.containsOnce(fixture, "nav .o_menu_toggle");
|
||||
assert.isVisible(fixture.querySelector("nav .o_menu_toggle"));
|
||||
assert.strictEqual(fixture.querySelector(".o_action_manager").innerHTML, "");
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: "__test__client__action__",
|
||||
menu_id: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user