合并企业版代码(未测试,先提交到测试分支)

This commit is contained in:
qihao.gong@jikimo.com
2023-04-14 17:42:23 +08:00
parent 7a7b3d7126
commit d28525526a
1300 changed files with 513579 additions and 5426 deletions

View File

@@ -0,0 +1,87 @@
odoo.define('web_studio.ReportEditorAction_tests', function (require) {
"use strict";
const { controlPanel } = require('web.test_utils');
const { getPagerValue, pagerNext } = controlPanel;
const { getFixture } = require("@web/../tests/helpers/utils");
const { doAction } = require("@web/../tests/webclient/helpers");
const { openStudio, registerStudioDependencies,getReportServerData } = require("@web_studio/../tests/helpers");
const { createEnterpriseWebClient } = require("@web_enterprise/../tests/helpers");
let serverData;
let target;
QUnit.module('Studio', {
beforeEach: function () {
this.data = {
foo: {
fields: {},
records: [{ id: 22 }, { id: 23 }],
},
"ir.actions.report": {
fields: { model: { type: "char" }, report_name: { type: "char" }, report_type:{ type: "char" }},
records: [{ id: 11, model: "foo", report_name: "foo_report", report_type: "pdf" }],
},
"ir.model": {
fields: {},
},
};
const reportServerData = getReportServerData();
const actions = {
1: {
id: 1,
xml_id: "kikou.action",
name: 'Kikou Action',
res_model: 'foo',
type: 'ir.actions.act_window',
view_mode: 'list,form',
views: [[1, 'form']],
}
};
const views = Object.assign({
"foo,2,form": `<form><field name="display_name" /></form>`,
"foo,false,search": `<search />`,
}, reportServerData.views);
serverData = {actions, models: this.data, views};
Object.assign(serverData.models, reportServerData.models);
registerStudioDependencies();
target = getFixture();
},
}, function () {
QUnit.module('ReportEditorAction');
QUnit.test('use pager', async function (assert) {
assert.expect(2);
const reportHTML = `
<html>
<head/>
<body>
<div id="wrapwrap">
<main>
<div class="page"/>
</main>
</div>
</body>
</html>`;
const mockRPC = (route, args) => {
switch (route) {
case "/web_studio/get_report_views":
return { report_html: reportHTML };
case "/web_studio/get_widgets_available_options":
case "/web_studio/read_paperformat":
return {};
}
};
const webClient = await createEnterpriseWebClient({ serverData, mockRPC });
await doAction(webClient, 1);
await openStudio(target, {report: 11});
assert.strictEqual(getPagerValue(target), "1");
await pagerNext(target);
assert.strictEqual(getPagerValue(target), "2");
});
});
});

View File

@@ -0,0 +1,800 @@
odoo.define('web_studio.ReportEditorComponents_tests', function (require) {
"use strict";
var testUtils = require('web.test_utils');
var studioTestUtils = require('web_studio.testUtils');
var Widget = require('web.Widget');
var studioTestUtils = require('web_studio.testUtils');
var editComponentsRegistry = require('web_studio.reportEditComponentsRegistry');
var reportNewComponentsRegistry = require('web_studio.reportNewComponentsRegistry');
QUnit.module('Studio', {}, function () {
QUnit.module('ReportComponents', {
before: function() {
return new Promise(function (resolve, reject) {
studioTestUtils.createSidebar({}).then(function (sidebar) {
sidebar.destroy();
resolve();
});
});
},
beforeEach: function () {
this.widgetsOptions = {
monetary: {
company_id: {
type: "model",
string: "Company",
description: "Company used for the original currency (only used for t-esc)",
default_value: "Company used to render the template",
params: "res.company"
},
date: {
type: "date",
string: "Date",
description: "Date used for the original currency (only used for t-esc)",
default_value: "Current date"
},
from_currency: {
type: "model",
string: "Original currency",
params: "res.currency"
},
display_currency: {
type: "model",
string: "Display currency",
required: "value_to_html",
params: "res.currency"
}
},
relative: {
now: {
type: "datetime",
string: "Reference date",
description: "Date to compare with the field value.",
default_value: "Current date"
}
},
image: {},
text: {},
html: {},
many2many: {},
date: {
format: {
type: "string",
string: "Date format"
}
},
datetime: {
time_only: {
type: "boolean",
string: "Display only the time"
},
hide_seconds: {
type: "boolean",
string: "Hide seconds"
},
format: {
type: "string",
string: "Pattern to format"
}
},
qweb: {},
many2one: {},
integer: {},
float_time: {},
contact: {
separator: {
type: "selection",
params : {
type: "selection",
selection: [
[" ", "Space"],
[",", "Comma"],
["-", "Dash"],
["|", "Vertical bar"],
["/", "Slash"]
],
placeholder: 'Linebreak',
},
string: "Address separator",
description: "Separator use to split the addresse from the display_name.",
default_value: false,
},
no_marker: {
type: "boolean",
string: "Hide marker",
description: "Don't display the font awsome marker"
},
country_image: {
type: "boolean",
string: "Displayed contry image",
description: "Display the country image if the field is present on the record"
},
fields: {
type: "array",
string: "Displayed fields",
description: "List of contact fields to display in the widget",
default_value: [
"name",
"address",
"phone",
"mobile",
"email"
],
params: {
type: "selection",
params: [
{'field_name': 'name', 'label': 'Name'},
{'field_name': 'address', 'label': 'Address'},
{'field_name': 'phone', 'label': 'Phone'},
{'field_name': 'mobile', 'label': 'Mobile'},
{'field_name': 'email', 'label': 'Email'},
{'field_name': 'vat', 'label': 'VAT'},
]
}
},
no_tag_br: {
type: "boolean",
string: "Use comma",
description: "Use comma instead of the <br> tag to display the address"
},
phone_icons: {
type: "boolean",
string: "Displayed phone icons",
description: "Display the phone icons even if no_marker is True"
}
},
duration: {
unit: {
type: "select",
string: "Date unit",
description: "Date unit used for comparison and formatting",
default_value: "hour",
params: [
[ "year", "year" ],
[ "month", "month" ],
[ "week", "week" ],
[ "day", "day" ],
[ "hour", "hour" ],
[ "minute", "minute" ],
[ "second", "second" ]
]
},
round: {
type: "select",
string: "Rounding unit",
description: "Date unit used for the rounding. If the value is given, this must be smaller than the unit",
default_value: "Same unit as \"unit\" option",
params: [
[ "year", "year" ],
[ "month", "month" ],
[ "week", "week" ],
[ "day", "day" ],
[ "hour", "hour" ],
[ "minute", "minute" ],
[ "second", "second" ]
]
}
},
selection: {
selection: {
type: "selection",
string: "Selection",
default_value: "Use the field information",
required: true
}
},
barcode: {
type: {
type: "string",
string: "Barcode type",
description: "Barcode type, eg: UPCA, EAN13, Code128",
default_value: "Code128"
},
width: {
type: "integer",
string: "Width",
default_value: 600
},
height: {
type: "integer",
string: "Height",
default_value: 100
},
humanreadable: {
type: "integer",
string: "Human Readable",
default_value: 0
}
},
float: {
precision: {
type: "integer",
string: "Rounding precision"
}
}
};
this.data = studioTestUtils.getData({
'model.test': {
fields: {
name: {string: "Name", type: "char"},
image: {string: "Image", type: "binary"},
child: {string: "Child", type: 'many2one', relation: 'model.test.child', searchable: true},
child_bis: {string: "Child Bis", type: 'many2one', relation: 'model.test.child', searchable: true},
children: {string: "Children", type: 'many2many', relation: 'model.test.child', searchable: true},
},
records: [],
},
'model.test.child': {
fields: {
name: { string: "Name", type: "char" },
company_id: { string: "Company", type: "many2one", relation: 'res.company', searchable: true },
currency_id: { string: "Currency", type: "many2one", relation: 'res.currency', searchable: true },
date: { string: "Date", type: "datetime", searchable: true },
},
records: [],
},
'res.company': {
fields: {
name: { string: "Name", type: "char" },
},
records: [],
},
'res.currency': {
fields: {
name: { string: "Name", type: "char" },
},
records: [],
},
});
studioTestUtils.patch();
},
afterEach: function () {
studioTestUtils.unpatch();
},
}, function () {
QUnit.module('New');
QUnit.test('field', async function (assert) {
assert.expect(2);
var parent = new Widget();
await testUtils.mock.addMockEnvironment(parent, {
data: this.data,
});
await parent.appendTo($('#qunit-fixture'));
var InlineField = reportNewComponentsRegistry.get('Inline')[1];
var tOptions = new InlineField(parent, {
models: {
'model.test': 'Toto',
},
});
tOptions.add({
targets: [{
data: {},
node: {
attrs: {
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
contextOrder: ['toto'],
context: {
toto: 'model.test',
},
parent: {
children: [],
attrs: {},
}
},
}]
}).then(function (res) {
assert.deepEqual(res.inheritance,
[{content: '<span t-field="toto.child"></span>', xpath: '/my/node/path/', view_id: 99, position: undefined}],
"Should send the operation");
});
await testUtils.nextTick();
await testUtils.dom.triggerEvents($('.o_web_studio_field_modal .o_field_selector'), ['focus']);
await testUtils.dom.click($('.o_web_studio_field_modal .o_field_selector_close'));
await testUtils.dom.click($('.o_web_studio_field_modal .btn-primary'));
assert.strictEqual($('.modal main[role="alert"]').length, 1,
"Should display an alert because the field name of the record is wrong");
await testUtils.dom.click($('.modal:has(main[role="alert"]) .btn-primary'));
await testUtils.dom.triggerEvents($('.o_web_studio_field_modal .o_field_selector'), ['focus']);
await testUtils.dom.click($('.o_web_studio_field_modal .o_field_selector_item[data-name="child"]'));
await testUtils.dom.click($('.o_web_studio_field_modal .btn-primary'));
parent.destroy();
});
QUnit.test('add a binary field', async function (assert) {
assert.expect(1);
var parent = new Widget();
await testUtils.mock.addMockEnvironment(parent, {
data: this.data,
});
await parent.appendTo($('#qunit-fixture'));
var InlineField = reportNewComponentsRegistry.get('Inline')[1];
var tOptions = new InlineField(parent, {
models: {
'model.test': 'Kikou',
},
});
tOptions.add({
targets: [{
data: {},
node: {
attrs: {
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
contextOrder: ['toto'],
context: {
toto: 'model.test',
},
parent: {
children: [],
attrs: {},
}
},
}]
}).then(function (res) {
assert.deepEqual(res.inheritance,
[{content: '<span t-field="toto.image" t-options-widget="&quot;image&quot;"></span>', xpath: '/my/node/path/', view_id: 99, position: undefined}],
"image widget should be set");
});
await testUtils.nextTick();
await testUtils.dom.triggerEvents($('.o_web_studio_field_modal .o_field_selector'), ['focus']);
await testUtils.dom.click($('.o_web_studio_field_modal .o_field_selector_item[data-name="image"]'));
await testUtils.dom.click($('.o_web_studio_field_modal .btn-primary'));
parent.destroy();
});
QUnit.module('Edit');
QUnit.test('column component with valid classes', async function (assert) {
assert.expect(2);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
var column = new (editComponentsRegistry.get('column'))(parent, {
node: {
attrs: {
class: 'col-5 offset-3',
},
},
});
await column.appendTo(parent.$el);
assert.strictEqual(column.$('input[name="size"]').val(), "5",
"the size should be correctly set");
assert.strictEqual(column.$('input[name="offset"]').val(), "3",
"the offset should be correctly set");
parent.destroy();
});
QUnit.test('column component with invalid classes', async function (assert) {
assert.expect(2);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
var column = new (editComponentsRegistry.get('column'))(parent, {
node: {
attrs: {
class: 'col- offset-kikou',
},
},
});
await column.appendTo(parent.$el);
assert.strictEqual(column.$('input[name="size"]').val(), "",
"the size should be unkown");
assert.strictEqual(column.$('input[name="offset"]').val(), "",
"the offset should be unkown");
parent.destroy();
});
QUnit.test('hidden "width" for layout component with col nodes', async function (assert) {
assert.expect(1);
var parent = new Widget();
await testUtils.mock.addMockEnvironment(parent, this);
await parent.appendTo($('#qunit-fixture'));
var layout = new (editComponentsRegistry.get('layout'))(parent, {
node: {
tag: 'div',
attrs: {
class: 'col- offset-kikou',
},
$nodes: $(),
},
});
await layout.appendTo(parent.$el);
assert.containsNone(layout.$('.o_web_studio_width'),
"the width attribute shouldn't be displayed for div.col nodes");
parent.destroy();
});
QUnit.test('tOptions component', async function (assert) {
assert.expect(3);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
var tOptions = new (editComponentsRegistry.get('tOptions'))(parent, {
widgetsOptions: this.widgetsOptions,
node: {
attrs: {
't-options': '{"widget": "text"}',
't-options-widget': '"image"',
't-options-other-options': 'True',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
},
context: {},
state: null,
models: null,
});
await tOptions.appendTo(parent.$el);
assert.strictEqual(tOptions.$('select').val(), 'image',
"Should select the image widget");
assert.containsNone(tOptions, '.o_web_studio_toption_option',
"there should be no available option");
// unset the `widget`
await testUtils.mock.addMockEnvironment(parent, {
intercepts: {
view_change: function (ev) {
assert.deepEqual(ev.data.operation.new_attrs, {'t-options-widget': '""'},
"should correctly delete the group");
},
},
});
tOptions.$('select').val('').trigger('change');
parent.destroy();
});
QUnit.test('tOptions component parse expression', async function (assert) {
assert.expect(5);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
var fields = this.data['model.test.child'].fields;
fields.company_id = {string: "Company", type: "many2one", relation: 'res.company', searchable: true};
fields.currency_id = {string: "Currency", type: "many2one", relation: 'res.currency', searchable: true};
fields.date = {string: "Date", type: "datetime", searchable: true};
await testUtils.mock.addMockEnvironment(parent, {
data: this.data,
});
var tOptions = new (editComponentsRegistry.get('tOptions'))(parent, {
widgetsOptions: this.widgetsOptions,
node: {
attrs: {
't-options': 'dict(from_currency=o.child.currency_id, date=o.child.date)',
't-options-widget': '"monetary"',
't-options-company_id': 'o.child.company_id',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
context: {"o": "model.test"},
},
context: {},
state: null,
models: {"model.test": "Model Test"},
});
await tOptions.appendTo(parent.$el);
assert.strictEqual(tOptions.$('select').val(), 'monetary',
"Should select the image widget");
assert.containsN(tOptions, '.o_web_studio_toption_option', 4,
"there should be 4 available options for the monetary widget");
assert.strictEqual(tOptions.$('.o_web_studio_toption_option_monetary_from_currency .o_field_selector_value').text().replace(/\s+/g, ''),
"o(ModelTest)ChildCurrency",
"Should display the currency field");
assert.strictEqual(tOptions.$('.o_web_studio_toption_option_monetary_date .o_field_selector_value').text().replace(/\s+/g, ''),
"o(ModelTest)ChildDate",
"Should display the data field");
assert.strictEqual(tOptions.$('.o_web_studio_toption_option_monetary_company_id .o_field_selector_value').text().replace(/\s+/g, ''),
"o(ModelTest)ChildCompany",
"Should display the company field");
parent.destroy();
});
QUnit.test('tEsc component with parsable expression', async function (assert) {
assert.expect(1);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
await testUtils.mock.addMockEnvironment(parent, {
data: this.data,
});
var tOptions = new (editComponentsRegistry.get('tEsc'))(parent, {
node: {
attrs: {
't-esc': 'o.child.company_id',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
context: {"o": "model.test"},
},
context: {},
state: null,
models: {"model.test": "Model Test"},
});
await tOptions.appendTo(parent.$el);
await testUtils.nextTick();
// the component value is parsable so we display it with ModelFieldSelector
assert.strictEqual(tOptions.$('.o_field_selector_value').text().replace(/\s+/g, ''),
"o(ModelTest)ChildCompany",
"Should display the company field");
parent.destroy();
});
QUnit.test('tEsc component with non-parsable expression', async function (assert) {
assert.expect(1);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
await testUtils.mock.addMockEnvironment(parent, {
data: this.data,
});
var tOptions = new (editComponentsRegistry.get('tEsc'))(parent, {
node: {
attrs: {
't-esc': 'o.child.getCompany()',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
context: {"o": "model.test"},
},
context: {},
state: null,
models: {"model.test": "Model Test"},
});
await tOptions.appendTo(parent.$el);
await testUtils.nextTick();
// the component can not parse the value so we display a simple input
assert.strictEqual(tOptions.$('input[name="t-esc"]').val(),
"o.child.getCompany()",
"Should display the company field");
parent.destroy();
});
QUnit.test('contact: many2many_select', async function (assert) {
assert.expect(11);
var parent = new Widget();
$('ul.ui-autocomplete').remove(); // clean the body to avoid errors due to another test
var optionsFields;
await testUtils.mock.addMockEnvironment(parent, {
intercepts: {
view_change: function (ev) {
assert.deepEqual(ev.data.operation.new_attrs['t-options-fields'], optionsFields,
'Should save the contact options');
params.node.attrs['t-options-fields'] = JSON.stringify(ev.data.operation.new_attrs['t-options-fields']);
},
},
});
await parent.appendTo($('#qunit-fixture'));
var params = {
widgetsOptions: this.widgetsOptions,
node: {
attrs: {
't-options': '{"widget": "contact"}',
't-options-no_marker': 'True',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
},
context: {},
state: null,
models: null,
};
var tOptions = new (editComponentsRegistry.get('tOptions'))(parent, params);
await tOptions.appendTo(parent.$el);
assert.containsN(tOptions, '.o_web_studio_toption_option', 3,
"there should be 3 available options for the contact widget (they are filtered)");
assert.strictEqual(tOptions.$('.o_badge_text').text(), 'NameAddressPhoneMobileEmail', 'Should display default value');
await testUtils.dom.click(tOptions.$('.o_input_dropdown input'));
assert.strictEqual($('ul.ui-autocomplete .ui-menu-item').length, 1, 'Should not display the unselected items');
assert.strictEqual($('ul.ui-autocomplete .o_m2o_dropdown_option').length, 0, 'Should not display create button');
optionsFields = ["name", "address", "phone", "mobile", "email", "vat"];
await testUtils.dom.click($('ul.ui-autocomplete .ui-menu-item:contains(VAT)'));
tOptions.destroy();
tOptions = new (editComponentsRegistry.get('tOptions'))(parent, params);
await tOptions.appendTo(parent.$el);
assert.strictEqual(tOptions.$('.o_badge_text').text(), 'NameAddressPhoneMobileEmailVAT', 'Should display the new value');
await testUtils.dom.click(tOptions.$('.o_input_dropdown input'));
assert.strictEqual($('ul.ui-autocomplete .ui-menu-item').length, 0, 'Should not display the unselected items');
await testUtils.dom.click(tOptions.$('.o_input_dropdown input'));
optionsFields = ["address", "phone", "mobile", "email", "vat"];
await testUtils.dom.click(tOptions.$('.o_field_many2manytags .o_delete:first'));
assert.strictEqual(tOptions.$('.o_badge_text').text(), 'AddressPhoneMobileEmailVAT', 'Should display the new value without "name"');
optionsFields = ["phone", "mobile", "email", "vat"];
await testUtils.dom.click(tOptions.$('.o_field_many2manytags .o_delete:first'));
assert.strictEqual(tOptions.$('.o_badge_text').text(), 'PhoneMobileEmailVAT', 'Should display the new value without "address"');
parent.destroy();
});
QUnit.test('contact: address separator', async function (assert) {
assert.expect(3);
var parent = new Widget();
var addressSeparator;
await testUtils.mock.addMockEnvironment(parent, {
intercepts: {
view_change: function (ev) {
assert.strictEqual(ev.data.operation.new_attrs['t-options-separator'], addressSeparator,
'Should save the selected address separator');
},
},
});
await parent.appendTo($('#qunit-fixture'));
var params = {
widgetsOptions: this.widgetsOptions,
node: {
attrs: {
't-options': '{"widget": "contact"}',
't-options-no_marker': 'True',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
},
context: {},
state: null,
models: null,
};
var tOptions = new (editComponentsRegistry.get('tOptions'))(parent, params);
await tOptions.appendTo(parent.$el);
var separators = _.map(tOptions.$('.o_web_studio_toption_option_contact_separator .o_field_widget option'), function (option) {
return JSON.parse($(option).val());
});
assert.deepEqual(separators, [false, " ", ",", "-", "|", "/"], 'There should be a selection field with proper values');
assert.strictEqual(tOptions.$('.o_web_studio_toption_option_contact_separator .o_field_widget option:selected').text(), 'Linebreak',
'Default value should be "LineBrak"');
addressSeparator = '","';
await testUtils.fields.editSelect(tOptions.$('.o_web_studio_toption_option_contact_separator .o_field_widget'), addressSeparator);
parent.destroy();
});
QUnit.test('contact: no_marker boolean field', async function (assert) {
assert.expect(2);
var parent = new Widget();
await testUtils.mock.addMockEnvironment(parent, {
intercepts: {
view_change: function (ev) {
assert.strictEqual(ev.data.operation.new_attrs["t-options-no_marker"], false,
'Toggling no_marker checkbox should change the option value');
},
},
});
await parent.appendTo($('#qunit-fixture'));
var params = {
widgetsOptions: this.widgetsOptions,
node: {
attrs: {
't-options': '{"widget": "contact"}',
't-options-no_marker': 'True',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
},
context: {},
state: null,
models: null,
};
var tOptions = new (editComponentsRegistry.get('tOptions'))(parent, params);
await tOptions.appendTo(parent.$el);
assert.containsOnce(tOptions, '.o_web_studio_toption_option_contact_no_marker input:checked',
"no_marker checkbox is checked initially");
await testUtils.dom.click(tOptions.$('.o_web_studio_toption_option_contact_no_marker input'));
parent.destroy();
});
QUnit.test('no search more in many2many_select', async function (assert) {
assert.expect(3);
var parent = new Widget();
$('ul.ui-autocomplete').remove(); // clean the body to avoid errors due to another test
await parent.appendTo($('#qunit-fixture'));
// to display more options in the many2many_select
this.widgetsOptions.contact.fields.default_value = [];
var tOptions = new (editComponentsRegistry.get('tOptions'))(parent, {
widgetsOptions: this.widgetsOptions,
node: {
attrs: {
't-options': '{"widget": "contact"}',
't-options-no_marker': 'True',
'data-oe-id': 99,
'data-oe-xpath': '/my/node/path/',
},
},
context: {},
state: null,
models: null,
});
await tOptions.appendTo(parent.$el);
assert.strictEqual(tOptions.$('.o_badge_text').text(), '', 'Should display default value');
await testUtils.dom.click(tOptions.$('.o_input_dropdown input'));
assert.strictEqual($('ul.ui-autocomplete .ui-menu-item').length, 6 , 'Should not display the unselected items');
assert.strictEqual($('ul.ui-autocomplete .o_m2o_dropdown_option').length, 0, 'Should not display create button nor the search more');
parent.destroy();
});
QUnit.test('groups component', async function (assert) {
assert.expect(3);
var parent = new Widget();
await parent.appendTo($('#qunit-fixture'));
var groups = new (editComponentsRegistry.get('groups'))(parent, {
widgets: this.widgets,
node: {
tag: 'span',
attrs: {
studio_groups: "[" +
"{\"name\": \"group_A\", \"display_name\": \"My Awesome Group\", \"id\": 42}," +
"{\"name\": \"group_13\", \"display_name\": \"Kikou\", \"id\": 13}" +
"]",
},
},
});
await groups.appendTo(parent.$el);
assert.containsN(groups, '.o_field_many2manytags .o_badge_text', 2,
"there should be displayed two groups");
assert.strictEqual(groups.$('.o_field_many2manytags').text().replace(/\s/g, ''), "MyAwesomeGroupKikou",
"the groups should be correctly set");
// delete a group
await testUtils.mock.addMockEnvironment(parent, {
intercepts: {
view_change: function (ev) {
assert.deepEqual(ev.data.operation.new_attrs, {groups: [13]},
"should correctly delete the group");
},
},
});
await testUtils.dom.click(groups.$('.o_field_many2manytags .o_delete:first'));
parent.destroy();
});
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,914 @@
odoo.define('web_studio.ReportEditorSidebar_tests', function (require) {
"use strict";
const { nextTick } = require("@web/../tests/helpers/utils");
var testUtils = require('web.test_utils');
var studioTestUtils = require('web_studio.testUtils');
QUnit.module('Studio', {}, function () {
QUnit.module('ReportEditorSidebar', {
beforeEach: function () {
this.data = {
'report.paperformat': {
fields: {
display_name: {string: "Name", type: "char"},
},
records: [{
id: 42,
display_name: 'My Awesome Format',
}],
},
'res.groups': {
fields: {
display_name: {string: "Name", type: "char"},
},
records: [{
id: 6,
display_name: 'Group6',
}, {
id: 7,
display_name: 'Group7',
}],
},
'x_mymodel': {
fields: {
display_name: {string: "Name", type: "char"},
},
},
};
this.widgetsOptions = {
image: {},
integer: {},
text: {},
};
},
}, function () {
QUnit.test("basic rendering", async function (assert) {
var done = assert.async();
assert.expect(5);
studioTestUtils.createSidebar({
state: { mode: 'report' },
report: {},
}).then(async function (sidebar) {
assert.hasClass(sidebar.$('.o_web_studio_sidebar_header [name="report"]'),'active',
"the report tab should be active");
assert.hasClass(sidebar.$('.o_web_studio_sidebar_header [name="options"]'),'inactive',
"the options tab should be inactive");
testUtils.mock.intercept(sidebar, 'sidebar_tab_changed', function (ev) {
assert.step(ev.data.mode);
});
testUtils.dom.click(sidebar.$('.o_web_studio_sidebar_header [name="new"]'));
assert.verifySteps(['new'], "the sidebar should be updated");
await testUtils.dom.click(sidebar.$('.o_web_studio_sidebar_header [name="options"]'));
assert.verifySteps([], "one should not be able to select options");
sidebar.destroy();
done();
});
});
QUnit.test("'Report' tab behaviour", async function (assert) {
assert.expect(6);
return studioTestUtils.createSidebar({
data: this.data,
state: { mode: 'report' },
report: {
name: 'Kikou',
},
}).then(async function (sidebar) {
assert.hasAttrValue(sidebar.$('.o_web_studio_sidebar_header > .active'), 'name', "report",
"the 'Report' tab should be active");
assert.strictEqual(sidebar.$('input[name="name"]').val(), "Kikou",
"the report name should be displayed");
testUtils.mock.intercept(sidebar, 'studio_edit_report', function (ev) {
if (ev.data.name) {
assert.deepEqual(ev.data, { name: "wow_report" });
} else if ('paperformat_id' in ev.data) {
paperformatValues.push(ev.data);
} else if (ev.data.groups_id) {
assert.deepEqual(ev.data, { groups_id: [7] });
}
});
// edit report name
sidebar.$('input[name="name"]').val("wow_report").trigger('change');
// edit the report paperformat
var paperformatValues = [];
await testUtils.fields.many2one.clickOpenDropdown('paperformat_id');
await testUtils.fields.many2one.clickHighlightedItem('paperformat_id');
assert.deepEqual(paperformatValues, [{ paperformat_id: 42 }]);
// remove the report paperformat
sidebar.$('[name="paperformat_id"] input').val('').trigger('keyup').trigger('focusout');
await testUtils.nextTick();
assert.deepEqual(paperformatValues, [{ paperformat_id: 42 }, { paperformat_id: false }]);
// edit groups
await testUtils.fields.many2one.clickOpenDropdown('groups_id');
await testUtils.fields.many2one.clickItem('groups_id', 'Group7');
sidebar.destroy();
});
});
QUnit.test("'Add' tab behaviour", function (assert) {
var done = assert.async();
assert.expect(2);
studioTestUtils.createSidebar({
state: { mode: 'new' },
}).then(function (sidebar) {
assert.hasAttrValue(sidebar.$('.o_web_studio_sidebar_header > .active'), 'name', "new",
"the 'Add' tab should be active");
assert.ok(sidebar.$('.ui-draggable').length,
"there should be draggable components");
sidebar.destroy();
done();
});
});
QUnit.test("basic 'Options' tab behaviour", function (assert) {
var done = assert.async();
assert.expect(4);
var node = {
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/div',
},
tag: 'span',
$nodes: $(),
},
};
studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: [node],
},
}).then(function (sidebar) {
assert.hasAttrValue(sidebar.$('.o_web_studio_sidebar_header > .active'), 'name', "options",
"the 'Options' tab should be active");
assert.containsOnce(sidebar, '.o_web_studio_sidebar_content .collapse',
"there should be one node in the accordion");
assert.hasClass(sidebar.$('.o_web_studio_sidebar_content .collapse'),'show',
"the node should be expanded by default");
// remove the element
testUtils.mock.intercept(sidebar, 'element_removed', function (ev) {
assert.deepEqual(ev.data.node, node.node);
});
testUtils.dom.click(sidebar.$('.o_web_studio_sidebar_content .collapse .o_web_studio_remove'));
sidebar.destroy();
done();
});
});
QUnit.test("'Options' tab with multiple nodes", async function (assert) {
assert.expect(9);
var node1 = {
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/div',
},
tag: 'span',
$nodes: $(),
},
};
var node2 = {
node: {
attrs: {
'data-oe-id': '40',
'data-oe-xpath': '/t/t',
},
tag: 'div',
$nodes: $(),
},
};
const sidebar = await studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: [node1, node2],
},
});
assert.hasAttrValue(sidebar.$('.o_web_studio_sidebar_header > .active'), 'name', "options",
"the 'Options' tab should be active");
assert.containsN(sidebar, '.o_web_studio_sidebar_content .card', 2,
"there should be one node in the accordion");
assert.hasClass(sidebar.$('.o_web_studio_sidebar_content .card:has(.o_text:contains(span)) .collapse'),'show',
"the 'span' node should be expanded by default");
assert.doesNotHaveClass(sidebar.$('.o_web_studio_sidebar_content .card:has(.o_text:contains(div)) .collapse'), 'show',
"the 'div' node shouldn't be expanded");
assert.strictEqual(sidebar.$('.o_web_studio_sidebar_content .o_web_studio_accordion > .card:last .card-header:first').text().trim(), "span",
"the last node should be the span");
// expand the first node
// BS4 collapsing is asynchronous
await new Promise((resolve) => {
$(document.body).one("hidden.bs.collapse", () => {
resolve();
});
testUtils.dom.click(sidebar.$('.o_web_studio_sidebar_content .o_web_studio_accordion > .card:first [data-bs-toggle="collapse"]:first'));
})
// await end of transitions: https://getbootstrap.com/docs/5.0/components/collapse/#example
await nextTick()
assert.doesNotHaveClass(sidebar.$('.o_web_studio_sidebar_content .card:has(.o_text:contains(span)) .collapse:first'), 'show',
"the 'span' node should have been closed");
assert.hasClass(sidebar.$('.o_web_studio_sidebar_content .card:has(.o_text:contains(div)) .collapse:first'),'show',
"the 'div' node should be expanded");
// reexpand the second node
await new Promise((resolve) => {
$(document.body).one("shown.bs.collapse", () => {
resolve();
});
testUtils.dom.click(sidebar.$('.o_web_studio_sidebar_content .o_web_studio_accordion > .card:last [data-bs-toggle="collapse"]:first'));
})
await nextTick();
assert.hasClass(sidebar.$('.o_web_studio_sidebar_content .card:has(.o_text:contains(span)) .collapse:first'),'show',
"the 'span' node should be expanded again");
assert.doesNotHaveClass(sidebar.$('.o_web_studio_sidebar_content .card:has(.o_text:contains(div)) .collapse:first'), 'show',
"the 'div' node shouldn't be expanded anymore");
sidebar.destroy();
});
QUnit.test("'Options' tab with layout component can be expanded", function (assert) {
var done = assert.async();
assert.expect(3);
var node = {
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/div',
},
tag: 'span',
$nodes: $(),
},
};
studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: [node],
},
}).then(function (sidebar) {
assert.containsOnce(sidebar, '.o_web_studio_sidebar_content .collapse',
"there should be one node in the accordion");
assert.containsOnce(sidebar, '.o_web_studio_sidebar_content .o_web_studio_layout',
"there should be a layout component");
assert.containsOnce(sidebar, '.o_web_studio_sidebar_content .o_web_studio_layout .o_web_studio_margin',
"there should be a margin section in the layout component");
sidebar.destroy();
done();
});
});
QUnit.test("'Options' tab with layout component can be expanded on open ", function (assert) {
var done = assert.async();
assert.expect(1);
var node = {
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/div',
},
tag: 'span',
$nodes: $(),
},
};
studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: [node],
},
previousState: {
"42/t/t/div": { 'layout': { showAll: true } }, // opens the layout expanded
},
}).then(function (sidebar) {
assert.equal(sidebar.$('.o_web_studio_width:visible').length, 1);
sidebar.destroy();
done();
});
});
QUnit.test("'Options' tab with layout component can be expanded on open with hierarchy", function (assert) {
var done = assert.async();
assert.expect(2);
var nodes = [
{
context: {'rec': 'int'},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/t/t/div',
't-esc': 'rec',
},
tag: 'span',
$nodes: $(),
}
},
{
context: {'rec': 'int'},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/t/t',
't-if': 'o.is_ok',
},
tag: 'div',
$nodes: $(),
},
},
{
context: {'rec': 'int'},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/t',
't-else': '',
},
tag: 't',
$nodes: $(),
},
},
{
context: {},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t',
't-foreach': '5',
't-as': 'rec',
},
tag: 't',
$nodes: $(),
},
},
{
context: {},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t',
't-name': 'my.template',
},
tag: 't',
$nodes: $(),
},
},
];
nodes[0].node.parent = nodes[1].node;
nodes[1].node.children = [nodes[0].node]
nodes[1].node.parent = nodes[2].node;
nodes[2].node.children = [nodes[1].node]
nodes[2].node.parent = nodes[3].node;
nodes[3].node.children = [{tag: 'span', attrs: {'t-if': 'false'}, $nodes: $(), parent: nodes[3].node}, nodes[2].node]
nodes[3].node.parent = nodes[4].node;
nodes[4].node.children = [nodes[3].node]
nodes[4].node.parent = null;
studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: nodes,
},
previousState: {
"42/t/t/t/t/div": { 'layout': { showAll: true } }, // opens the layout expanded
},
}).then(function (sidebar) {
assert.equal(sidebar.$('.card').length, 5, 'Should have 5 item');
assert.equal(sidebar.$('.card h5 > button').get().map(el => el.textContent.replace(/[\s\n]+/g, ' ').trim()).join(' > '), 't > t [foreach="5"] > t > div > span [rec]', 'Should have all ordered parents');
sidebar.destroy();
done();
});
});
QUnit.test("'Options' tab with widget selection (tOptions) component", function (assert) {
var done = assert.async();
assert.expect(4);
var node = {
context: {
'doc': 'x_mymodel',
},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/div',
't-field': 'doc.id',
't-options-widget': '"text"',
},
tag: 'span',
$nodes: $(),
},
};
studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: [node],
},
widgetsOptions: this.widgetsOptions,
}).then(function (sidebar) {
assert.containsOnce(sidebar, '.o_web_studio_tfield_fieldexpression',
"the t-field component should be displayed");
assert.containsOnce(sidebar, '.o_web_studio_toption_widget',
"the t-options component should be displayed");
assert.strictEqual(sidebar.$('.o_web_studio_toption_widget select').text().replace(/\s/g, ''), "imageintegertext",
"all widgets should be selectable");
assert.strictEqual(sidebar.$('.o_web_studio_toption_widget select').val(), "text",
"the correct widget should be selected");
sidebar.destroy();
done();
});
});
QUnit.test("'Options' tab with FieldSelector does not flicker", async function (assert) {
assert.expect(3);
var def = testUtils.makeTestPromise();
var node = {
context: {
'doc': 'x_mymodel',
},
node: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t/t/div',
't-field': 'doc.id',
't-options-widget': '"text"',
},
context: {
'doc': 'x_mymodel',
},
tag: 'span',
$nodes: $(),
},
};
var sidebarDef = studioTestUtils.createSidebar({
data: this.data,
models: {
'x_mymodel': 'My Model',
},
state: {
mode: 'properties',
nodes: [node],
},
widgetsOptions: this.widgetsOptions,
mockRPC: function (route, args) {
if (args.model === 'x_mymodel' && args.method === 'fields_get') {
// Block the 'read' call
var result = this._super.apply(this, arguments);
return Promise.resolve(def).then(_.constant(result));
}
return this._super.apply(this, arguments);
},
});
await testUtils.nextTick();
assert.strictEqual($('.o_web_studio_tfield_fieldexpression').length, 0,
"the sidebar should wait its components to be rendered before its insertion");
// release the fields_get
def.resolve();
var sidebar = await sidebarDef;
await testUtils.nextTick();
assert.strictEqual($('.o_web_studio_tfield_fieldexpression').length, 1,
"the t-field component should be displayed");
assert.strictEqual(sidebar.$('.o_web_studio_tfield_fieldexpression .o_field_selector_value').text().replace(/\s/g, ''),
"doc(MyModel)ID",
"the field chain should be correctly displayed");
sidebar.destroy();
});
QUnit.test('Various layout changes', function (assert) {
var done = assert.async();
// this test is a combinaison of multiple tests, to avoid copy
// pasting multiple times de sidebar create/intercept/destroy
var layoutChangeNode = {
attrs: {
'data-oe-id': '99',
'data-oe-xpath': '/t/t/div',
},
tag: 'div',
$nodes: $(),
};
var layoutChangeTextNode = {
attrs: {
'data-oe-id': '99',
'data-oe-xpath': '/t/t/span',
},
tag: 'span',
$nodes: $(),
};
var nodeWithAllLayoutPropertiesSet = {
tag: "div",
attrs: {
//width: "1",
style: "margin-top:2px;width:1px;margin-right:3px;margin-bottom:4px;margin-left:5px;",
class: "o_bold o_italic h3 bg-o-color-3 text-o-color-2 o_underline",
'data-oe-id': '99',
'data-oe-xpath': '/t/t/div',
},
$nodes: $(),
};
var nodeWithAllLayoutPropertiesFontAndBackgroundSet = {
tag: "div",
attrs: {
//width: "1",
style: "margin-top:2px;margin-right:3px;width:1px;margin-bottom:4px;margin-left:5px;background-color:#00FF00;color:#00FF00",
class: "o_bold o_italic h3 o_underline",
'data-oe-id': '99',
'data-oe-xpath': '/t/t/div',
},
$nodes: $(),
};
var layoutChangesOperations = [
{
testName: "add a margin top in pixels",
nodeToUse: layoutChangeNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-top"]',
valueToPut: "42",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"margin-top:42px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "add a margin bottom in pixels",
nodeToUse: layoutChangeNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-bottom"]',
valueToPut: "42",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"margin-bottom:42px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "add a margin left in pixels",
nodeToUse: layoutChangeNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-left"]',
valueToPut: "42",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"margin-left:42px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "add a margin right in pixels",
nodeToUse: layoutChangeNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-right"]',
valueToPut: "42",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"margin-right:42px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "add a width",
nodeToUse: layoutChangeNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_width input',
valueToPut: "42",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"width:42px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "add a width on a text",
nodeToUse: layoutChangeTextNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_width input',
valueToPut: "42",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"width:42px;display:inline-block\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/span"
}]
}
}, {
testName: "add a class",
nodeToUse: layoutChangeNode,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_classes input',
valueToPut: "new_class",
expectedRPC: {
new_attrs: {
class: "new_class"
},
type: "attributes",
},
}, {
testName: "set the heading level",
nodeToUse: layoutChangeNode,
eventToTrigger: "click",
sidebarOperationInputSelector: '.o_web_studio_font_size .dropdown-item-text[data-value="h3"]',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"class\" separator=\" \" add=\"h3\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "set the background color to a theme color",
nodeToUse: layoutChangeNode,
eventToTrigger: "mousedown",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_background_colorpicker button[data-color="o-color-3"]',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"class\" separator=\" \" add=\"bg-o-color-3\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "set the background color to a standard color",
nodeToUse: layoutChangeNode,
eventToTrigger: "mousedown",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_background_colorpicker button[data-value="#00FF00"]',
valueToPut: "h3",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"background-color:#00FF00\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "set the font color to a theme color",
nodeToUse: layoutChangeNode,
eventToTrigger: "mousedown",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_font_colorpicker button[data-color="o-color-3"]',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"class\" separator=\" \" add=\"text-o-color-3\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "set the font color to a standard color",
nodeToUse: layoutChangeNode,
eventToTrigger: "mousedown",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_font_colorpicker button[data-value="#00FF00"]',
valueToPut: "h3",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" add=\"color:#00FF00\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "set the alignment",
nodeToUse: layoutChangeNode,
eventToTrigger: "click",
sidebarOperationInputSelector: '.o_web_studio_text_alignment button[title="end"]',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"class\" separator=\" \" add=\"text-end\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "remove margin top in pixels",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-top"]',
valueToPut: "",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"margin-top:2px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "remove a margin bottom in pixels",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-bottom"]',
valueToPut: "",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"margin-bottom:4px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "remove a margin left in pixels",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-left"]',
valueToPut: "",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"margin-left:5px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "remove a margin right in pixels",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_margin [data-margin="margin-right"]',
valueToPut: "",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"margin-right:3px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "remove the width",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_width input',
valueToPut: "",
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"width:1px\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
}
}, {
testName: "remove a class",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "change",
sidebarOperationInputSelector: '.o_web_studio_classes input',
valueToPut: "o_bold o_italic bg-o-color-3 text-o-color-2 o_underline",
expectedRPC: {
new_attrs: {
class: "o_bold o_italic bg-o-color-3 text-o-color-2 o_underline"
},
type: "attributes",
},
}, {
testName: "unset the background color to a theme color",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "click",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_background_colorpicker .o_web_studio_reset_color',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"class\" separator=\" \" remove=\"bg-o-color-3\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
},{
testName: "unset the background color to a standard color",
nodeToUse: nodeWithAllLayoutPropertiesFontAndBackgroundSet,
eventToTrigger: "click",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_background_colorpicker .o_web_studio_reset_color',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"background-color:#00FF00\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "unset the font color to a theme color",
nodeToUse: nodeWithAllLayoutPropertiesSet,
eventToTrigger: "click",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_font_colorpicker .o_web_studio_reset_color',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"class\" separator=\" \" remove=\"text-o-color-2\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
}, {
testName: "unset the font color to a standard color",
nodeToUse: nodeWithAllLayoutPropertiesFontAndBackgroundSet,
eventToTrigger: "click",
sidebarOperationInputSelector: '.o_web_studio_colors .o_web_studio_font_colorpicker button.o_web_studio_reset_color',
expectedRPC: {
inheritance: [{
content: "<attribute name=\"style\" separator=\";\" remove=\"color:#00FF00\"/>",
position: "attributes",
view_id: 99,
xpath: "/t/t/div"
}]
},
},
];
// there is one assert by operation
assert.expect(layoutChangesOperations.length);
var initialDebugMode = odoo.debug;
// show 'class' in the sidebar
odoo.debug = true;
function poll (changeOperation) {
var node = {
node: changeOperation.nodeToUse,
};
studioTestUtils.createSidebar({
state: {
mode: 'properties',
nodes: [node],
},
previousState: {
"99/t/t/div": { 'layout': { showAll: true } }, // opens the layout expanded
},
}).then(function (sidebar) {
testUtils.mock.intercept(sidebar, 'view_change', function (ev) {
assert.deepEqual(ev.data.operation, changeOperation.expectedRPC, changeOperation.testName);
});
sidebar.$(changeOperation.sidebarOperationInputSelector)
.val(changeOperation.valueToPut)
.trigger(changeOperation.eventToTrigger);
sidebar.destroy();
}).then(function () {
if (layoutChangesOperations.length) {
poll(layoutChangesOperations.shift());
} else {
odoo.debug = initialDebugMode;
done();
}
});
}
poll(layoutChangesOperations.shift());
});
});
});
});

View File

@@ -0,0 +1,46 @@
odoo.define('web_studio.ReportEditor_tests', function (require) {
"use strict";
var studioTestUtils = require('web_studio.testUtils');
QUnit.module('Studio', {}, function () {
QUnit.module('ReportEditor', {
beforeEach: function () {
},
}, function () {
QUnit.test('basic report rendering', async function (assert) {
assert.expect(2);
var nodesArchs = {
42: {
attrs: {
'data-oe-id': '42',
'data-oe-xpath': '/t',
name: "Layout",
't-name': '42',
},
},
id: 42,
key: 'report.layout',
parent: null,
tag: 't',
};
var reportHTML = "<html><body><t/></body></html>";
var editor = await studioTestUtils.createReportEditor({
nodesArchs: nodesArchs,
reportHTML: reportHTML,
});
assert.containsOnce(editor, 'iframe',
"an iframe should be rendered");
assert.hasAttrValue(editor.$('iframe'), 'src', "about:blank",
"the source should be correctly set");
editor.destroy();
});
});
});
});