合并企业版代码(未测试,先提交到测试分支)
This commit is contained in:
@@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user