This rev. introduces robust helpers to use in the JS tests suite to
interact with DOM and components, and starts using them (almost)
everywhere.
All the helpers are exposed though testUtils.js.
There are 2 kinds of helpers:
1. Assertions
-------------
* assert.containsNone, containsN, containsOnce check that the DOM
(or a specific part of the DOM) contains a `selector`. It
generates a correct error message automatically.
ex: assert.strictEqual(form.$('.o_form_editable'), 1, "msg");
-> assert.containsOnce(form, '.o_form_editable');
* assert.isVisible, isNotVisible check that the DOM has an element
visible or not. They also check that the element is actually in
the DOM (before most tests didn't verify this).
* assert.hasClass, doesNotHaveClass, hasAttrVAlue, check specific
properties of a DOM element, and also validate that it is
applied on a single existing DOM element (before most tests
didn't verify this).
ex: assert.notOk(form.$('button').hasClass('btn-primary'));
-> assert.doesNotHaveClass(form.$('button'), 'btn-primary');
2. Utilities
------------
The goal of the utilities is to centralize the definition of many
standard components and interactions, ensuring that when we
refactor the JS framework, we do not need to change all the tests.
Existing mock utilities (addMockEnvironment, intercept, path,
patchDate, unpatch and fieldsViewGet) are moved to
'testUtils.mock.*'.
Existing DOM utilities are moved to 'testUtils.dom.*'.
New dom utilities are created for opendDatePicker, click,
clickFirst and clickLast. Helper `click` verifies that there is
exactly 1 element visible in the DOM you click on, `clickFirst`
and `clickLast` verify that there are more than one element on the
DOM.
ex: form.$('button').click();
-> testUtils.dom.click(form.$('button'));
New Form utilities: (testUtils.form.*)
clickEdit, clickSave, clickCreate, clickDiscard, all clicks on
the control panel buttons of the form.
`reload` reloads the form data.
New modal, graph, kanban and pivot utils (testUtils.pivot.*,
testUtils.kanban.*, etc.).
New fields utils: (testUtils.fields.*)
* editInput, editSelect: allow to change the value of a field,
using a selector to identify it. They validate that the input
exists and trigger the change event automatically.
* editAndTrigger: allow to modify a field and trigger specific
events after the value change
* many2one (testUtils.fields.many2one.*)
clickOpenDropdown, clickHighlightedItem, clickItem,
searchAndClickItem: use a field name instead of a selector and
do all the complex mechanism to open, filter and highlight
many2one fields.
Joint work with aab, dam, ged, mge, svs and vsc.
3680 lines
133 KiB
JavaScript
3680 lines
133 KiB
JavaScript
odoo.define('web.action_manager_tests', function (require) {
|
|
"use strict";
|
|
|
|
var ReportClientAction = require('report.client_action');
|
|
var NotificationService = require('web.NotificationService');
|
|
var AbstractAction = require('web.AbstractAction');
|
|
var AbstractStorageService = require('web.AbstractStorageService');
|
|
var BasicFields = require('web.basic_fields');
|
|
var ControlPanelMixin = require('web.ControlPanelMixin');
|
|
var core = require('web.core');
|
|
var ListController = require('web.ListController');
|
|
var StandaloneFieldManagerMixin = require('web.StandaloneFieldManagerMixin');
|
|
var RamStorage = require('web.RamStorage');
|
|
var ReportService = require('web.ReportService');
|
|
var SessionStorageService = require('web.SessionStorageService');
|
|
var testUtils = require('web.test_utils');
|
|
var Widget = require('web.Widget');
|
|
var createActionManager = testUtils.createActionManager;
|
|
|
|
QUnit.module('ActionManager', {
|
|
beforeEach: function () {
|
|
this.data = {
|
|
partner: {
|
|
fields: {
|
|
foo: {string: "Foo", type: "char"},
|
|
bar: {string: "Bar", type: "many2one", relation: 'partner'},
|
|
},
|
|
records: [
|
|
{id: 1, display_name: "First record", foo: "yop", bar: 2},
|
|
{id: 2, display_name: "Second record", foo: "blip", bar: 1},
|
|
{id: 3, display_name: "Third record", foo: "gnap", bar: 1},
|
|
{id: 4, display_name: "Fourth record", foo: "plop", bar: 2},
|
|
{id: 5, display_name: "Fifth record", foo: "zoup", bar: 2},
|
|
],
|
|
},
|
|
pony: {
|
|
fields: {
|
|
name: {string: 'Name', type: 'char'},
|
|
},
|
|
records: [
|
|
{id: 4, name: 'Twilight Sparkle'},
|
|
{id: 6, name: 'Applejack'},
|
|
{id: 9, name: 'Fluttershy'}
|
|
],
|
|
},
|
|
};
|
|
|
|
this.actions = [{
|
|
id: 1,
|
|
name: 'Partners Action 1',
|
|
res_model: 'partner',
|
|
type: 'ir.actions.act_window',
|
|
views: [[1, 'kanban']],
|
|
}, {
|
|
id: 2,
|
|
type: 'ir.actions.server',
|
|
}, {
|
|
id: 3,
|
|
name: 'Partners',
|
|
res_model: 'partner',
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'list'], [1, 'kanban'], [false, 'form']],
|
|
}, {
|
|
id: 4,
|
|
name: 'Partners Action 4',
|
|
res_model: 'partner',
|
|
type: 'ir.actions.act_window',
|
|
views: [[1, 'kanban'], [2, 'list'], [false, 'form']],
|
|
}, {
|
|
id: 5,
|
|
name: 'Create a Partner',
|
|
res_model: 'partner',
|
|
target: 'new',
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'form']],
|
|
}, {
|
|
id: 6,
|
|
name: 'Partner',
|
|
res_id: 2,
|
|
res_model: 'partner',
|
|
target: 'inline',
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'form']],
|
|
}, {
|
|
id: 7,
|
|
name: "Some Report",
|
|
report_name: 'some_report',
|
|
report_type: 'qweb-pdf',
|
|
type: 'ir.actions.report',
|
|
}, {
|
|
id: 8,
|
|
name: 'Favorite Ponies',
|
|
res_model: 'pony',
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'list'], [false, 'form']],
|
|
}, {
|
|
id: 9,
|
|
name: 'A Client Action',
|
|
tag: 'ClientAction',
|
|
type: 'ir.actions.client',
|
|
}, {
|
|
id: 10,
|
|
type: 'ir.actions.act_window_close',
|
|
}, {
|
|
id: 11,
|
|
name: "Another Report",
|
|
report_name: 'another_report',
|
|
report_type: 'qweb-pdf',
|
|
type: 'ir.actions.report',
|
|
close_on_report_download: true,
|
|
}];
|
|
|
|
this.archs = {
|
|
// kanban views
|
|
'partner,1,kanban': '<kanban><templates><t t-name="kanban-box">' +
|
|
'<div class="oe_kanban_global_click"><field name="foo"/></div>' +
|
|
'</t></templates></kanban>',
|
|
|
|
// list views
|
|
'partner,false,list': '<tree><field name="foo"/></tree>',
|
|
'partner,2,list': '<tree limit="3"><field name="foo"/></tree>',
|
|
'pony,false,list': '<tree><field name="name"/></tree>',
|
|
|
|
// form views
|
|
'partner,false,form': '<form>' +
|
|
'<header>' +
|
|
'<button name="object" string="Call method" type="object"/>' +
|
|
'<button name="4" string="Execute action" type="action"/>' +
|
|
'</header>' +
|
|
'<group>' +
|
|
'<field name="display_name"/>' +
|
|
'<field name="foo"/>' +
|
|
'</group>' +
|
|
'</form>',
|
|
'pony,false,form': '<form>' +
|
|
'<field name="name"/>' +
|
|
'</form>',
|
|
|
|
// search views
|
|
'partner,false,search': '<search><field name="foo" string="Foo"/></search>',
|
|
'partner,1,search': '<search>' +
|
|
'<filter name="bar" help="Bar" domain="[(\'bar\', \'=\', 1)]"/>' +
|
|
'</search>',
|
|
'pony,false,search': '<search></search>',
|
|
};
|
|
},
|
|
}, function () {
|
|
QUnit.module('Misc');
|
|
|
|
QUnit.test('breadcrumbs and actions with target inline', function (assert) {
|
|
assert.expect(3);
|
|
|
|
this.actions[3].views = [[false, 'form']];
|
|
this.actions[3].target = 'inline';
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
actionManager.doAction(4);
|
|
assert.ok(!$('.o_control_panel').is(':visible'),
|
|
"control panel should not be visible");
|
|
|
|
actionManager.doAction(1, {clear_breadcrumbs: true});
|
|
assert.ok($('.o_control_panel').is(':visible'),
|
|
"control panel should now be visible");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb').text(), "Partners Action 1",
|
|
"should have only one current action visible in breadcrumbs");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('no widget memory leaks when doing some action stuff', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var delta = 0;
|
|
testUtils.mock.patch(Widget, {
|
|
init: function () {
|
|
delta++;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
destroy: function () {
|
|
delta--;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(8);
|
|
|
|
var n = delta;
|
|
actionManager.doAction(4);
|
|
// kanban view is loaded, switch to list view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
// go back to action 7 in breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:first'));
|
|
|
|
assert.strictEqual(delta, n,
|
|
"should have properly destroyed all other widgets");
|
|
actionManager.destroy();
|
|
testUtils.mock.unpatch(Widget);
|
|
});
|
|
|
|
QUnit.test('no widget memory leaks when executing actions in dialog', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var delta = 0;
|
|
testUtils.mock.patch(Widget, {
|
|
init: function () {
|
|
delta++;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
destroy: function () {
|
|
if (!this.isDestroyed()) {
|
|
delta--;
|
|
}
|
|
this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
var n = delta;
|
|
|
|
actionManager.doAction(5);
|
|
actionManager.doAction({type: 'ir.actions.act_window_close'});
|
|
|
|
assert.strictEqual(delta, n,
|
|
"should have properly destroyed all widgets");
|
|
|
|
actionManager.destroy();
|
|
testUtils.mock.unpatch(Widget);
|
|
});
|
|
|
|
QUnit.test('no memory leaks when executing an action while switching view', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var def;
|
|
var delta = 0;
|
|
testUtils.mock.patch(Widget, {
|
|
init: function () {
|
|
delta += 1;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
destroy: function () {
|
|
delta -= 1;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
if (args.method === 'read') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
actionManager.doAction(4);
|
|
var n = delta;
|
|
|
|
actionManager.doAction(3, {clear_breadcrumbs: true});
|
|
|
|
// switch to the form view (this request is blocked)
|
|
def = $.Deferred();
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
// execute another action meanwhile (don't block this request)
|
|
actionManager.doAction(4, {clear_breadcrumbs: true});
|
|
|
|
// unblock the switch to the form view in action 3
|
|
def.resolve();
|
|
|
|
assert.strictEqual(n, delta,
|
|
"all widgets of action 3 should have been destroyed");
|
|
|
|
actionManager.destroy();
|
|
testUtils.mock.unpatch(Widget);
|
|
});
|
|
|
|
QUnit.test('no memory leaks when executing an action while loading views', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var def;
|
|
var delta = 0;
|
|
testUtils.mock.patch(Widget, {
|
|
init: function () {
|
|
delta += 1;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
destroy: function () {
|
|
delta -= 1;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
if (args.method === 'load_views') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
// execute action 4 to know the number of widgets it instantiates
|
|
actionManager.doAction(4);
|
|
var n = delta;
|
|
|
|
// execute a first action (its 'load_views' RPC is blocked)
|
|
def = $.Deferred();
|
|
actionManager.doAction(3, {clear_breadcrumbs: true});
|
|
|
|
// execute another action meanwhile (and unlock the RPC)
|
|
actionManager.doAction(4, {clear_breadcrumbs: true});
|
|
def.resolve();
|
|
|
|
assert.strictEqual(n, delta,
|
|
"all widgets of action 3 should have been destroyed");
|
|
|
|
actionManager.destroy();
|
|
testUtils.mock.unpatch(Widget);
|
|
});
|
|
|
|
QUnit.test('no memory leaks when executing an action while loading data of default view', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var def;
|
|
var delta = 0;
|
|
testUtils.mock.patch(Widget, {
|
|
init: function () {
|
|
delta += 1;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
destroy: function () {
|
|
delta -= 1;
|
|
this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route) {
|
|
var result = this._super.apply(this, arguments);
|
|
if (route === '/web/dataset/search_read') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
// execute action 4 to know the number of widgets it instantiates
|
|
actionManager.doAction(4);
|
|
var n = delta;
|
|
|
|
// execute a first action (its 'search_read' RPC is blocked)
|
|
def = $.Deferred();
|
|
actionManager.doAction(3, {clear_breadcrumbs: true});
|
|
|
|
// execute another action meanwhile (and unlock the RPC)
|
|
actionManager.doAction(4, {clear_breadcrumbs: true});
|
|
def.resolve();
|
|
|
|
assert.strictEqual(n, delta,
|
|
"all widgets of action 3 should have been destroyed");
|
|
|
|
actionManager.destroy();
|
|
testUtils.mock.unpatch(Widget);
|
|
});
|
|
|
|
QUnit.test('action with "no_breadcrumbs" set to true', function (assert) {
|
|
assert.expect(2);
|
|
|
|
_.findWhere(this.actions, {id: 4}).context = {no_breadcrumbs: true};
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
|
|
// push another action flagged with 'no_breadcrumbs=true'
|
|
actionManager.doAction(4);
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 0,
|
|
"the breadcrumbs should be empty");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('on_reverse_breadcrumb handler is correctly called', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
// execute action 3 and open a record in form view
|
|
actionManager.doAction(3);
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
// execute action 4 without 'on_reverse_breadcrumb' handler, then go back
|
|
actionManager.doAction(4);
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:first'));
|
|
assert.verifySteps([]);
|
|
|
|
// execute action 4 with an 'on_reverse_breadcrumb' handler, then go back
|
|
actionManager.doAction(4, {
|
|
on_reverse_breadcrumb: function () {
|
|
assert.step('on_reverse_breadcrumb');
|
|
}
|
|
});
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:first'));
|
|
assert.verifySteps(['on_reverse_breadcrumb']);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('handles "history_back" event', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
actionManager.doAction(4);
|
|
actionManager.doAction(3);
|
|
actionManager.trigger_up('history_back');
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners Action 4',
|
|
"breadcrumbs should display the display_name of the action");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('stores and restores scroll position', function (assert) {
|
|
assert.expect(7);
|
|
|
|
var left;
|
|
var top;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
getScrollPosition: function (ev) {
|
|
assert.step('getScrollPosition');
|
|
ev.data.callback({left: left, top: top});
|
|
},
|
|
scrollTo: function (ev) {
|
|
assert.step('scrollTo left ' + ev.data.left + ', top ' + ev.data.top);
|
|
},
|
|
},
|
|
});
|
|
|
|
// execute a first action and simulate a scroll
|
|
assert.step('execute action 3');
|
|
actionManager.doAction(3);
|
|
left = 50;
|
|
top = 100;
|
|
|
|
// execute a second action (in which we don't scroll)
|
|
assert.step('execute action 4');
|
|
actionManager.doAction(4);
|
|
|
|
// go back using the breadcrumbs
|
|
assert.step('go back to action 3');
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
|
|
assert.verifySteps([
|
|
'execute action 3',
|
|
'execute action 4',
|
|
'getScrollPosition', // of action 3, before leaving it
|
|
'go back to action 3',
|
|
'getScrollPosition', // of action 4, before leaving it
|
|
'scrollTo left 50, top 100', // restore scroll position of action 3
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Push State');
|
|
|
|
QUnit.test('properly push state', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var stateDescriptions = [
|
|
{action: 4, model: "partner", title: "Partners Action 4", view_type: "kanban"},
|
|
{action: 8, model: "pony", title: "Favorite Ponies", view_type: "list"},
|
|
{action: 8, id: 4, model: "pony", title: "Twilight Sparkle", view_type: "form"},
|
|
];
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
push_state: function (event) {
|
|
var descr = stateDescriptions.shift();
|
|
assert.deepEqual(_.extend({}, event.data.state), descr,
|
|
"should notify the environment of new state");
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(4);
|
|
actionManager.doAction(8);
|
|
testUtils.dom.click(actionManager.$('tr.o_data_row:first'));
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('push state after action is loaded, not before', function (assert) {
|
|
assert.expect(5);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
push_state: function () {
|
|
assert.step('push_state');
|
|
},
|
|
},
|
|
mockRPC: function (route) {
|
|
assert.step(route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(4);
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'/web/dataset/call_kw/partner',
|
|
'/web/dataset/search_read',
|
|
'push_state'
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('do not push state for actions in target=new', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
push_state: function () {
|
|
assert.step('push_state');
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(4);
|
|
assert.verifySteps(['push_state']);
|
|
actionManager.doAction(5);
|
|
assert.verifySteps(['push_state']);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('do not push state when action fails', function (assert) {
|
|
assert.expect(4);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
push_state: function () {
|
|
assert.step('push_state');
|
|
},
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'read') {
|
|
// this is the rpc to load form view
|
|
return $.Deferred().reject();
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(8);
|
|
assert.verifySteps(['push_state']);
|
|
testUtils.dom.click(actionManager.$('tr.o_data_row:first'));
|
|
assert.verifySteps(['push_state']);
|
|
// we make sure here that the list view is still in the dom
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"there should still be a list view in dom");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Load State');
|
|
|
|
QUnit.test('should not crash on invalid state', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
res_model: 'partner', // the valid key for the model is 'model', not 'res_model'
|
|
});
|
|
|
|
assert.strictEqual(actionManager.$el.text(), '', "should display nothing");
|
|
assert.verifySteps([]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('properly load client actions', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var ClientAction = AbstractAction.extend({
|
|
className: 'o_client_action_test',
|
|
start: function () {
|
|
this.$el.text('Hello World');
|
|
},
|
|
});
|
|
core.action_registry.add('HelloWorldTest', ClientAction);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
action: 'HelloWorldTest',
|
|
});
|
|
|
|
assert.strictEqual(actionManager.$('.o_client_action_test').text(),
|
|
'Hello World', "should have correctly rendered the client action");
|
|
|
|
assert.verifySteps([]);
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.HelloWorldTest;
|
|
});
|
|
|
|
QUnit.test('properly load act window actions', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
action: 1,
|
|
});
|
|
|
|
assert.strictEqual($('.o_control_panel').length, 1,
|
|
"should have rendered a control panel");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should have rendered a kanban view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('properly load records', function (assert) {
|
|
assert.expect(5);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
id: 2,
|
|
model: 'partner',
|
|
});
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should have rendered a form view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Second record',
|
|
"should have opened the second record");
|
|
|
|
assert.verifySteps([
|
|
'load_views',
|
|
'read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('load requested view for act window actions', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
action: 3,
|
|
view_type: 'kanban',
|
|
});
|
|
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not have rendered a list view");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should have rendered a kanban view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('lazy load multi record view if mono record one is requested', function (assert) {
|
|
assert.expect(11);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
action: 3,
|
|
id: 2,
|
|
view_type: 'form',
|
|
});
|
|
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not have rendered a list view");
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should have rendered a form view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Second record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
// go back to Lst
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should now display the list view");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should not display the form view anymore");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'read', // read the opened record
|
|
'/web/dataset/search_read', // search read when coming back to List
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('lazy load multi record view with previous action', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(4);
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb li').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb li').text(), 'Partners Action 4',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
actionManager.doAction(3, {
|
|
resID: 2,
|
|
viewType: 'form',
|
|
});
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb li').length, 3,
|
|
"there should be three controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb li').text(), 'Partners Action 4PartnersSecond record',
|
|
"the breadcrumb elements should be correctly ordered");
|
|
|
|
// go back to List
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:last'));
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb li').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb li').text(), 'Partners Action 4Partners',
|
|
"the breadcrumb elements should be correctly ordered");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('change the viewType of the current action', function (assert) {
|
|
assert.expect(13);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should have rendered a list view");
|
|
|
|
// switch to kanban view
|
|
actionManager.loadState({
|
|
action: 3,
|
|
view_type: 'kanban',
|
|
});
|
|
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not display the list view anymore");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should have switched to the kanban view");
|
|
|
|
// switch to form view, open record 4
|
|
actionManager.loadState({
|
|
action: 3,
|
|
id: 4,
|
|
view_type: 'form',
|
|
});
|
|
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"should not display the kanban view anymore");
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should have switched to the form view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Fourth record',
|
|
"should have opened the requested record");
|
|
|
|
// verify steps to ensure that the whole action hasn't been re-executed
|
|
// (if it would have been, /web/action/load and load_views would appear
|
|
// several times)
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list view
|
|
'/web/dataset/search_read', // kanban view
|
|
'read', // form view
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('change the id of the current action', function (assert) {
|
|
assert.expect(11);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
// execute action 3 and open the first record in a form view
|
|
actionManager.doAction(3);
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should have rendered a form view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'First record',
|
|
"should have opened the first record");
|
|
|
|
// switch to record 4
|
|
actionManager.loadState({
|
|
action: 3,
|
|
id: 4,
|
|
view_type: 'form',
|
|
});
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should still display the form view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Fourth record',
|
|
"should have switched to the requested record");
|
|
|
|
// verify steps to ensure that the whole action hasn't been re-executed
|
|
// (if it would have been, /web/action/load and load_views would appear
|
|
// twice)
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list view
|
|
'read', // form view, record 1
|
|
'read', // form view, record 4
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('should not push a loaded state', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
push_state: function () {
|
|
assert.step('push_state');
|
|
},
|
|
},
|
|
});
|
|
actionManager.loadState({action: 3});
|
|
|
|
assert.verifySteps([], "should not push the loaded state");
|
|
|
|
testUtils.dom.click(actionManager.$('tr.o_data_row:first'));
|
|
|
|
assert.verifySteps(['push_state'],
|
|
"should push the state of it changes afterwards");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('should not push a loaded state of a client action', function (assert) {
|
|
assert.expect(4);
|
|
|
|
var ClientAction = Widget.extend({
|
|
init: function (parent, action, options) {
|
|
this._super.apply(this, arguments);
|
|
this.controllerID = options.controllerID;
|
|
},
|
|
start: function () {
|
|
var self = this;
|
|
var $button = $('<button>').text('Click Me!');
|
|
$button.on('click', function () {
|
|
self.trigger_up('push_state', {
|
|
controllerID: self.controllerID,
|
|
state: {someValue: 'X'},
|
|
});
|
|
});
|
|
this.$el.append($button);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
core.action_registry.add('ClientAction', ClientAction);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
push_state: function (ev) {
|
|
assert.step('push_state');
|
|
assert.deepEqual(ev.data.state, {
|
|
action: 9,
|
|
someValue: 'X',
|
|
title: 'A Client Action',
|
|
});
|
|
},
|
|
},
|
|
});
|
|
actionManager.loadState({action: 9});
|
|
|
|
assert.verifySteps([], "should not push the loaded state");
|
|
|
|
testUtils.dom.click(actionManager.$('button'));
|
|
|
|
assert.verifySteps(['push_state'],
|
|
"should push the state of it changes afterwards");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('change a param of an ir.actions.client in the url', function (assert) {
|
|
assert.expect(7);
|
|
|
|
var ClientAction = AbstractAction.extend(ControlPanelMixin, {
|
|
className: 'o_client_action',
|
|
init: function (parent, action) {
|
|
this._super.apply(this, arguments);
|
|
var context = action.context;
|
|
this.a = context.params && context.params.a || 'default value';
|
|
},
|
|
start: function () {
|
|
assert.step('start');
|
|
this.$el.text(this.a);
|
|
this.trigger_up('push_state', {
|
|
controllerID: this.controllerID,
|
|
state: {a: this.a},
|
|
});
|
|
},
|
|
});
|
|
core.action_registry.add('ClientAction', ClientAction);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
// execute the client action
|
|
actionManager.doAction(9);
|
|
|
|
assert.strictEqual(actionManager.$('.o_client_action').text(), 'default value',
|
|
"should have rendered the client action");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
|
|
// update param 'a' in the url
|
|
actionManager.loadState({
|
|
action: 9,
|
|
a: 'new value',
|
|
});
|
|
|
|
assert.strictEqual(actionManager.$('.o_client_action').text(), 'new value',
|
|
"should have rerendered the client action with the correct param");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should still be one controller in the breadcrumbs");
|
|
|
|
// should have executed the client action twice
|
|
assert.verifySteps(['start', 'start']);
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.ClientAction;
|
|
});
|
|
|
|
QUnit.test('load a window action without id (in a multi-record view)', function (assert) {
|
|
assert.expect(14);
|
|
|
|
var RamStorageService = AbstractStorageService.extend({
|
|
storage: new RamStorage(),
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
session_storage: RamStorageService,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
testUtils.mock.intercept(actionManager, 'call_service', function (ev) {
|
|
if (ev.data.service === 'session_storage') {
|
|
assert.step(ev.data.method);
|
|
}
|
|
}, true);
|
|
|
|
actionManager.doAction(4);
|
|
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should display a kanban view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners Action 4',
|
|
"breadcrumbs should display the display_name of the action");
|
|
|
|
actionManager.loadState({
|
|
model: 'partner',
|
|
view_type: 'list',
|
|
});
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners Action 4',
|
|
"should still be in the same action");
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"should no longer display a kanban view");
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display a list view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/dataset/search_read', // action 3
|
|
'setItem', // action 3
|
|
'getItem', // loadState
|
|
'load_views', // loaded action
|
|
'/web/dataset/search_read', // loaded action
|
|
'setItem', // loaded action
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Concurrency management');
|
|
|
|
QUnit.test('drop previous actions if possible', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var def = $.Deferred();
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route) {
|
|
var result = this._super.apply(this, arguments);
|
|
assert.step(route);
|
|
if (route === '/web/action/load') {
|
|
return def.then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
actionManager.doAction(4);
|
|
actionManager.doAction(8);
|
|
|
|
def.resolve();
|
|
|
|
// action 4 loads a kanban view first, 6 loads a list view. We want a list
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
'there should be a list view in DOM');
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // load action 4
|
|
'/web/action/load', // load action 6
|
|
'/web/dataset/call_kw/pony', // load views for action 6
|
|
'/web/dataset/search_read', // search read for list view action 6
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('handle switching view and switching back on slow network', function (assert) {
|
|
assert.expect(8);
|
|
|
|
var def = $.Deferred();
|
|
var defs = [$.when(), def, $.when()];
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route) {
|
|
assert.step(route);
|
|
var result = this._super.apply(this, arguments);
|
|
if (route === '/web/dataset/search_read') {
|
|
var def = defs.shift();
|
|
return def.then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
actionManager.doAction(4);
|
|
|
|
// kanban view is loaded, switch to list view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
|
|
// here, list view is not ready yet, because def is not resolved
|
|
// switch back to kanban view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
|
|
// here, we want the kanban view to reload itself, regardless of list view
|
|
assert.verifySteps([
|
|
"/web/action/load", // initial load action
|
|
"/web/dataset/call_kw/partner", // load views
|
|
"/web/dataset/search_read", // search_read for kanban view
|
|
"/web/dataset/search_read", // search_read for list view (not resolved yet)
|
|
"/web/dataset/search_read" // search_read for kanban view reload (not resolved yet)
|
|
]);
|
|
|
|
// we resolve def => list view is now ready (but we want to ignore it)
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"there should be a kanban view in dom");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"there should not be a list view in dom");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('when an server action takes too much time...', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var def = $.Deferred();
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route) {
|
|
if (route === '/web/action/run') {
|
|
return def.then(_.constant(1));
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
actionManager.doAction(2);
|
|
actionManager.doAction(4);
|
|
|
|
def.resolve();
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item.active').text(), 'Partners Action 4',
|
|
'action 4 should be loaded');
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('clicking quickly on breadcrumbs...', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var def = $.when();
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
if (args.method === 'read') {
|
|
return def.then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
// create a situation with 3 breadcrumbs: kanban/form/list
|
|
actionManager.doAction(4);
|
|
testUtils.dom.click(actionManager.$('.o_kanban_record:first'));
|
|
actionManager.doAction(8);
|
|
|
|
// now, the next read operations will be deferred (this is the read
|
|
// operation for the form view reload)
|
|
def = $.Deferred();
|
|
|
|
// click on the breadcrumbs for the form view, then on the kanban view
|
|
// before the form view is fully reloaded
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb-item:eq(1)'));
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb-item:eq(0)'));
|
|
|
|
// resolve the form view read
|
|
def.resolve();
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item.active').text(), 'Partners Action 4',
|
|
'action 4 should be loaded and visible');
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('execute a new action while loading a lazy-loaded controller', function (assert) {
|
|
assert.expect(15);
|
|
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
assert.step(args.method || route);
|
|
if (route === '/web/dataset/search_read' && args.model === 'partner') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
actionManager.loadState({
|
|
action: 4,
|
|
id: 2,
|
|
view_type: 'form',
|
|
});
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should display the form view of action 4");
|
|
|
|
// click to go back to Kanban (this request is blocked)
|
|
def = $.Deferred();
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should still display the form view of action 4");
|
|
|
|
// execute another action meanwhile (don't block this request)
|
|
actionManager.doAction(8, {clear_breadcrumbs: true});
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display action 8");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should no longer display the form view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // load state action 4
|
|
'load_views', // load state action 4
|
|
'read', // read the opened record (action 4)
|
|
'/web/dataset/search_read', // blocked search read when coming back to Kanban (action 4)
|
|
'/web/action/load', // action 8
|
|
'load_views', // action 8
|
|
'/web/dataset/search_read', // search read action 8
|
|
]);
|
|
|
|
// unblock the switch to Kanban in action 4
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should still display action 8");
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"should not display the kanban view of action 4");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // load state action 4
|
|
'load_views', // load state action 4
|
|
'read', // read the opened record (action 4)
|
|
'/web/dataset/search_read', // blocked search read when coming back to Kanban (action 4)
|
|
'/web/action/load', // action 8
|
|
'load_views', // action 8
|
|
'/web/dataset/search_read', // search read action 8
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('execute a new action while handling a call_button', function (assert) {
|
|
assert.expect(16);
|
|
|
|
var self = this;
|
|
var def = $.Deferred();
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/web/dataset/call_button') {
|
|
return def.then(_.constant(self.actions[0]));
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
// execute action 3 and open a record in form view
|
|
actionManager.doAction(3);
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should display the form view of action 3");
|
|
|
|
// click on 'Call method' button (this request is blocked)
|
|
testUtils.dom.click(actionManager.$('.o_form_view button:contains(Call method)'));
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should still display the form view of action 3");
|
|
|
|
// execute another action
|
|
actionManager.doAction(8, {clear_breadcrumbs: true});
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view of action 8");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should no longer display the form view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/dataset/search_read', // list for action 3
|
|
'read', // form for action 3
|
|
'object', // click on 'Call method' button (this request is blocked)
|
|
'/web/action/load', // action 8
|
|
'load_views', // action 8
|
|
'/web/dataset/search_read', // list for action 8
|
|
]);
|
|
|
|
// unblock the call_button request
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should still display the list view of action 8");
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"should not display action 1");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/dataset/search_read', // list for action 3
|
|
'read', // form for action 3
|
|
'object', // click on 'Call method' button (this request is blocked)
|
|
'/web/action/load', // action 8
|
|
'load_views', // action 8
|
|
'/web/dataset/search_read', // list for action 8
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('execute a new action while switching to another controller', function (assert) {
|
|
assert.expect(15);
|
|
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
assert.step(args.method || route);
|
|
if (args.method === 'read') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view of action 3");
|
|
|
|
// switch to the form view (this request is blocked)
|
|
def = $.Deferred();
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should still display the list view of action 3");
|
|
|
|
// execute another action meanwhile (don't block this request)
|
|
actionManager.doAction(4, {clear_breadcrumbs: true});
|
|
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should display the kanban view of action 8");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should no longer display the list view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/dataset/search_read', // search read of list view of action 3
|
|
'read', // read the opened record of action 3 (this request is blocked)
|
|
'/web/action/load', // action 4
|
|
'load_views', // action 4
|
|
'/web/dataset/search_read', // search read action 4
|
|
]);
|
|
|
|
// unblock the switch to the form view in action 3
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should still display the kanban view of action 8");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should not display the form view of action 3");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/dataset/search_read', // search read of list view of action 3
|
|
'read', // read the opened record of action 3 (this request is blocked)
|
|
'/web/action/load', // action 4
|
|
'load_views', // action 4
|
|
'/web/dataset/search_read', // search read action 4
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('execute a new action while loading views', function (assert) {
|
|
assert.expect(10);
|
|
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
assert.step(args.method || route);
|
|
if (args.method === 'load_views') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
// execute a first action (its 'load_views' RPC is blocked)
|
|
def = $.Deferred();
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not display the list view of action 3");
|
|
|
|
// execute another action meanwhile (and unlock the RPC)
|
|
actionManager.doAction(4);
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should display the kanban view of action 4");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not display the list view of action 3");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/action/load', // action 4
|
|
'load_views', // action 4
|
|
'/web/dataset/search_read', // search read action 4
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('execute a new action while loading data of default view', function (assert) {
|
|
assert.expect(11);
|
|
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
assert.step(args.method || route);
|
|
if (route === '/web/dataset/search_read') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
// execute a first action (its 'search_read' RPC is blocked)
|
|
def = $.Deferred();
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not display the list view of action 3");
|
|
|
|
// execute another action meanwhile (and unlock the RPC)
|
|
actionManager.doAction(4);
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should display the kanban view of action 4");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not display the list view of action 3");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 3
|
|
'load_views', // action 3
|
|
'/web/dataset/search_read', // search read action 3
|
|
'/web/action/load', // action 4
|
|
'load_views', // action 4
|
|
'/web/dataset/search_read', // search read action 4
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('open a record while reloading the list view', function (assert) {
|
|
assert.expect(12);
|
|
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route) {
|
|
var result = this._super.apply(this, arguments);
|
|
if (route === '/web/dataset/search_read') {
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view");
|
|
assert.containsN(actionManager, '.o_list_view .o_data_row', 5,
|
|
"list view should contain 5 records");
|
|
assert.strictEqual($('.o_control_panel .o_list_buttons').length, 1,
|
|
"list view buttons should be displayed in control panel");
|
|
|
|
// reload (the search_read RPC will be blocked)
|
|
def = $.Deferred();
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
|
|
assert.containsN(actionManager, '.o_list_view .o_data_row', 5,
|
|
"list view should still contain 5 records");
|
|
assert.strictEqual($('.o_control_panel .o_list_buttons').length, 1,
|
|
"list view buttons should still be displayed in control panel");
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should display the form view");
|
|
assert.strictEqual($('.o_control_panel .o_list_buttons').length, 0,
|
|
"list view buttons should no longer be displayed in control panel");
|
|
assert.strictEqual($('.o_control_panel .o_form_buttons_view').length, 1,
|
|
"form view buttons should be displayed instead");
|
|
|
|
// unblock the search_read RPC
|
|
def.resolve();
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should display the form view");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should not display the list view");
|
|
assert.strictEqual($('.o_control_panel .o_list_buttons').length, 0,
|
|
"list view buttons should still not be displayed in control panel");
|
|
assert.strictEqual($('.o_control_panel .o_form_buttons_view').length, 1,
|
|
"form view buttons should still be displayed instead");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Client Actions');
|
|
|
|
QUnit.test('can execute client actions from tag name', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var ClientAction = AbstractAction.extend({
|
|
className: 'o_client_action_test',
|
|
start: function () {
|
|
this.$el.text('Hello World');
|
|
},
|
|
});
|
|
core.action_registry.add('HelloWorldTest', ClientAction);
|
|
|
|
var actionManager = createActionManager({
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
}
|
|
});
|
|
actionManager.doAction('HelloWorldTest');
|
|
|
|
assert.strictEqual($('.o_control_panel:visible').length, 0, // AAB: global selector until the ControlPanel is moved from ActionManager to the Views
|
|
"shouldn't have rendered a control panel");
|
|
assert.strictEqual(actionManager.$('.o_client_action_test').text(),
|
|
'Hello World', "should have correctly rendered the client action");
|
|
assert.verifySteps([]);
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.HelloWorldTest;
|
|
});
|
|
|
|
QUnit.test('client action with control panel', function (assert) {
|
|
assert.expect(4);
|
|
|
|
var ClientAction = AbstractAction.extend(ControlPanelMixin, {
|
|
className: 'o_client_action_test',
|
|
start: function () {
|
|
this.$el.text('Hello World');
|
|
this.set('title', 'Hello'); // AAB: drop this and replace by getTitle()
|
|
},
|
|
});
|
|
core.action_registry.add('HelloWorldTest', ClientAction);
|
|
|
|
var actionManager = createActionManager();
|
|
actionManager.doAction('HelloWorldTest');
|
|
|
|
assert.strictEqual($('.o_control_panel:visible').length, 1,
|
|
"should have rendered a control panel");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Hello',
|
|
"breadcrumbs should still display the title of the controller");
|
|
assert.strictEqual(actionManager.$('.o_client_action_test').text(),
|
|
'Hello World', "should have correctly rendered the client action");
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.HelloWorldTest;
|
|
});
|
|
|
|
QUnit.test('state is pushed for client actions', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var ClientAction = AbstractAction.extend(ControlPanelMixin, {
|
|
className: 'o_client_action_test',
|
|
start: function () {
|
|
this.$el.text('Hello World');
|
|
},
|
|
});
|
|
var actionManager = createActionManager({
|
|
intercepts: {
|
|
push_state: function () {
|
|
assert.step('push state');
|
|
},
|
|
},
|
|
});
|
|
core.action_registry.add('HelloWorldTest', ClientAction);
|
|
|
|
actionManager.doAction('HelloWorldTest');
|
|
|
|
assert.verifySteps(['push state']);
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.HelloWorldTest;
|
|
});
|
|
|
|
QUnit.test('breadcrumb is updated on title change', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var ClientAction = Widget.extend(ControlPanelMixin, {
|
|
className: 'o_client_action_test',
|
|
events: {
|
|
click: function () {
|
|
this.set("title", 'new title');
|
|
},
|
|
},
|
|
start: function () {
|
|
this.set("title", 'initial title');
|
|
this.$el.text('Hello World');
|
|
},
|
|
});
|
|
var actionManager = createActionManager();
|
|
core.action_registry.add('HelloWorldTest', ClientAction);
|
|
actionManager.doAction('HelloWorldTest');
|
|
|
|
assert.strictEqual($('ol.breadcrumb').text(), "initial title",
|
|
"should have initial title as breadcrumb content");
|
|
|
|
testUtils.dom.click(actionManager.$('.o_client_action_test'));
|
|
assert.strictEqual($('ol.breadcrumb').text(), "new title",
|
|
"should have updated title as breadcrumb content");
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.HelloWorldTest;
|
|
});
|
|
|
|
QUnit.module('Server actions');
|
|
|
|
QUnit.test('can execute server actions from db ID', function (assert) {
|
|
assert.expect(9);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/web/action/run') {
|
|
assert.strictEqual(args.action_id, 2,
|
|
"should call the correct server action");
|
|
return $.when(1); // execute action 1
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(2);
|
|
|
|
assert.strictEqual($('.o_control_panel:visible').length, 1,
|
|
"should have rendered a control panel");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should have rendered a kanban view");
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'/web/action/run',
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('handle server actions returning false', function (assert) {
|
|
assert.expect(9);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/web/action/run') {
|
|
return $.when(false);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
// execute an action in target="new"
|
|
actionManager.doAction(5, {
|
|
on_close: assert.step.bind(assert, 'close handler'),
|
|
});
|
|
assert.strictEqual($('.o_technical_modal .o_form_view').length, 1,
|
|
"should have rendered a form view in a modal");
|
|
|
|
// execute a server action that returns false
|
|
actionManager.doAction(2);
|
|
assert.strictEqual($('.o_technical_modal').length, 0,
|
|
"should have closed the modal");
|
|
assert.verifySteps([
|
|
'/web/action/load', // action 5
|
|
'load_views',
|
|
'default_get',
|
|
'/web/action/load', // action 2
|
|
'/web/action/run',
|
|
'close handler',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Report actions');
|
|
|
|
QUnit.test('can execute report actions from db ID', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
report: ReportService,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/report/check_wkhtmltopdf') {
|
|
return $.when('ok');
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
get_file: function (params) {
|
|
assert.step(params.url);
|
|
params.success();
|
|
params.complete();
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(7, {
|
|
on_close: function () {
|
|
assert.step('on_close');
|
|
},
|
|
});
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'/report/check_wkhtmltopdf',
|
|
'/web/static/src/img/spin.png', // block UI image
|
|
'/report/download',
|
|
'on_close',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('report actions can close modals and reload views', function (assert) {
|
|
assert.expect(8);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
report: ReportService,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (route === '/report/check_wkhtmltopdf') {
|
|
return $.when('ok');
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
get_file: function (params) {
|
|
assert.step(params.url);
|
|
params.success();
|
|
params.complete();
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
|
|
// load modal
|
|
actionManager.doAction(5, {
|
|
on_close: function () {
|
|
assert.step('on_close');
|
|
},
|
|
});
|
|
|
|
assert.strictEqual($('.o_technical_modal .o_form_view').length, 1,
|
|
"should have rendered a form view in a modal");
|
|
|
|
actionManager.doAction(7, {
|
|
on_close: function () {
|
|
assert.step('on_printed');
|
|
},
|
|
});
|
|
|
|
assert.strictEqual($('.o_technical_modal .o_form_view').length, 1,
|
|
"The modal should still exist");
|
|
|
|
actionManager.doAction(11);
|
|
|
|
assert.strictEqual($('.o_technical_modal .o_form_view').length, 0,
|
|
"the modal should have been closed after the action report");
|
|
|
|
assert.verifySteps([
|
|
'/report/download',
|
|
'on_printed',
|
|
'/report/download',
|
|
'on_close',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('should trigger a notification if wkhtmltopdf is to upgrade', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
report: ReportService,
|
|
notification: NotificationService.extend({
|
|
notify: function (params) {
|
|
assert.step(params.type || 'notification');
|
|
}
|
|
}),
|
|
},
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/report/check_wkhtmltopdf') {
|
|
return $.when('upgrade');
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
get_file: function (params) {
|
|
assert.step(params.url);
|
|
params.success();
|
|
params.complete();
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(7);
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'/report/check_wkhtmltopdf',
|
|
'notification',
|
|
'/web/static/src/img/spin.png', // block UI image
|
|
'/report/download',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('should open the report client action if wkhtmltopdf is broken', function (assert) {
|
|
assert.expect(6);
|
|
|
|
// patch the report client action to override its iframe's url so that
|
|
// it doesn't trigger an RPC when it is appended to the DOM (for this
|
|
// usecase, using removeSRCAttribute doesn't work as the RPC is
|
|
// triggered as soon as the iframe is in the DOM, even if its src
|
|
// attribute is removed right after)
|
|
testUtils.mock.patch(ReportClientAction, {
|
|
start: function () {
|
|
var self = this;
|
|
return this._super.apply(this, arguments).then(function () {
|
|
self.iframe.src = 'test ' + self.iframe.getAttribute('src');
|
|
});
|
|
}
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
report: ReportService,
|
|
notification: NotificationService.extend({
|
|
notify: function (params) {
|
|
assert.step(params.type || 'notification');
|
|
}
|
|
})
|
|
},
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/report/check_wkhtmltopdf') {
|
|
return $.when('broken');
|
|
}
|
|
if (route === 'test /report/html/some_report') {
|
|
return $.when();
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
get_file: function (params) {
|
|
assert.step(params.url); // should not be called
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(7);
|
|
|
|
assert.containsOnce(actionManager, '.o_report_iframe',
|
|
"should have opened the report client action");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'/report/check_wkhtmltopdf',
|
|
'notification',
|
|
'test /report/html/some_report', // report client action's iframe
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
testUtils.mock.unpatch(ReportClientAction);
|
|
});
|
|
|
|
QUnit.module('Window Actions');
|
|
|
|
QUnit.test('can execute act_window actions from db ID', function (assert) {
|
|
assert.expect(6);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(1);
|
|
|
|
assert.strictEqual($('.o_control_panel').length, 1,
|
|
"should have rendered a control panel");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should have rendered a kanban view");
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('can switch between views', function (assert) {
|
|
assert.expect(18);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view");
|
|
|
|
// switch to kanban view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should no longer display the list view");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should display the kanban view");
|
|
|
|
// switch back to list view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view");
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"should no longer display the kanban view");
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should no longer display the list view");
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should display the form view");
|
|
assert.strictEqual(actionManager.$('.o_field_widget[name=foo]').text(), 'yop',
|
|
"should have opened the correct record");
|
|
|
|
// go back to list view using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should no longer display the form view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list
|
|
'/web/dataset/search_read', // kanban
|
|
'/web/dataset/search_read', // list
|
|
'read', // form
|
|
'/web/dataset/search_read', // list
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('breadcrumbs are updated when switching between views', function (assert) {
|
|
assert.expect(10);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners',
|
|
"breadcrumbs should display the display_name of the action");
|
|
|
|
// switch to kanban view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should still be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners',
|
|
"breadcrumbs should still display the display_name of the action");
|
|
|
|
// switch back to list view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should still be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners',
|
|
"breadcrumbs should still display the display_name of the action");
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'First record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
// go back to list view using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Partners',
|
|
"breadcrumbs should display the display_name of the action");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('switch buttons are updated when switching between views', function (assert) {
|
|
assert.expect(13);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button').length, 2,
|
|
"should have two switch buttons (list and kanban)");
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button.active').length, 1,
|
|
"should have only one active button");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_buttons button:first'),'o_cp_switch_list',
|
|
"list switch button should be the first one");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_list'),'active',
|
|
"list should be the active view");
|
|
|
|
// switch to kanban view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button').length, 2,
|
|
"should still have two switch buttons (list and kanban)");
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button.active').length, 1,
|
|
"should still have only one active button");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_buttons button:first'),'o_cp_switch_list',
|
|
"list switch button should still be the first one");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_kanban'),'active',
|
|
"kanban should now be the active view");
|
|
|
|
// switch back to list view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button').length, 2,
|
|
"should still have two switch buttons (list and kanban)");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_list'),'active',
|
|
"list should now be the active view");
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button').length, 0,
|
|
"should not have any switch buttons");
|
|
|
|
// go back to list view using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
assert.strictEqual($('.o_control_panel .o_cp_switch_buttons button').length, 2,
|
|
"should have two switch buttons (list and kanban)");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_list'),'active',
|
|
"list should be the active view");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('pager is updated when switching between views', function (assert) {
|
|
assert.expect(10);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(4);
|
|
|
|
assert.strictEqual($('.o_control_panel .o_pager_value').text(), '1-5',
|
|
"value should be correct for kanban");
|
|
assert.strictEqual($('.o_control_panel .o_pager_limit').text(), '5',
|
|
"limit should be correct for kanban");
|
|
|
|
// switch to list view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
assert.strictEqual($('.o_control_panel .o_pager_value').text(), '1-3',
|
|
"value should be correct for list");
|
|
assert.strictEqual($('.o_control_panel .o_pager_limit').text(), '5',
|
|
"limit should be correct for list");
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual($('.o_control_panel .o_pager_value').text(), '1',
|
|
"value should be correct for form");
|
|
assert.strictEqual($('.o_control_panel .o_pager_limit').text(), '3',
|
|
"limit should be correct for form");
|
|
|
|
// go back to list view using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
assert.strictEqual($('.o_control_panel .o_pager_value').text(), '1-3',
|
|
"value should be correct for list");
|
|
assert.strictEqual($('.o_control_panel .o_pager_limit').text(), '5',
|
|
"limit should be correct for list");
|
|
|
|
// switch back to kanban view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
assert.strictEqual($('.o_control_panel .o_pager_value').text(), '1-5',
|
|
"value should be correct for kanban");
|
|
assert.strictEqual($('.o_control_panel .o_pager_limit').text(), '5',
|
|
"limit should be correct for kanban");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('there is no flickering when switching between views', function (assert) {
|
|
assert.expect(20);
|
|
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function () {
|
|
var result = this._super.apply(this, arguments);
|
|
return $.when(def).then(_.constant(result));
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// switch to kanban view
|
|
def = $.Deferred();
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should still display the list view");
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"shouldn't display the kanban view yet");
|
|
def.resolve();
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"shouldn't display the list view anymore");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should now display the kanban view");
|
|
|
|
// switch back to list view
|
|
def = $.Deferred();
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_list'));
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should still display the kanban view");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"shouldn't display the list view yet");
|
|
def.resolve();
|
|
assert.containsNone(actionManager, '.o_kanban_view',
|
|
"shouldn't display the kanban view anymore");
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should now display the list view");
|
|
|
|
// open a record in form view
|
|
def = $.Deferred();
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should still display the list view");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"shouldn't display the form view yet");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should still be one controller in the breadcrumbs");
|
|
def.resolve();
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"should no longer display the list view");
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should display the form view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
|
|
// go back to list view using the breadcrumbs
|
|
def = $.Deferred();
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should still display the form view");
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"shouldn't display the list view yet");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should still be two controllers in the breadcrumbs");
|
|
def.resolve();
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should no longer display the form view");
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('breadcrumbs are updated when display_name changes', function (assert) {
|
|
assert.expect(4);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'First record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
// switch to edit mode and change the display_name
|
|
testUtils.dom.click($('.o_control_panel .o_form_button_edit'));
|
|
testUtils.fields.editInput(actionManager.$('.o_field_widget[name=display_name]'), 'New name');
|
|
testUtils.dom.click($('.o_control_panel .o_form_button_save'));
|
|
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should still be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'New name',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('reload previous controller when discarding a new record', function (assert) {
|
|
assert.expect(8);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// create a new record
|
|
testUtils.dom.click($('.o_control_panel .o_list_button_add'));
|
|
assert.containsOnce(actionManager, '.o_form_view.o_form_editable',
|
|
"should have opened the form view in edit mode");
|
|
|
|
// discard
|
|
testUtils.dom.click($('.o_control_panel .o_form_button_cancel'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should have switched back to the list view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list
|
|
'default_get', // form
|
|
'/web/dataset/search_read', // list
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('requests for execute_action of type object are handled', function (assert) {
|
|
assert.expect(10);
|
|
|
|
var self = this;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
if (route === '/web/dataset/call_button') {
|
|
assert.deepEqual(args, {
|
|
args: [[1], {some_key: 2}],
|
|
method: 'object',
|
|
model: 'partner',
|
|
}, "should call route with correct arguments");
|
|
var record = _.findWhere(self.data.partner.records, {id: args.args[0][0]});
|
|
record.foo = 'value changed';
|
|
return $.when(false);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {user_context: {
|
|
some_key: 2,
|
|
}},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual(actionManager.$('.o_field_widget[name=foo]').text(), 'yop',
|
|
"check initial value of 'yop' field");
|
|
|
|
// click on 'Call method' button (should call an Object method)
|
|
testUtils.dom.click(actionManager.$('.o_form_view button:contains(Call method)'));
|
|
assert.strictEqual(actionManager.$('.o_field_widget[name=foo]').text(), 'value changed',
|
|
"'yop' has been changed by the server, and should be updated in the UI");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list for action 3
|
|
'read', // form for action 3
|
|
'object', // click on 'Call method' button
|
|
'read', // re-read form view
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('requests for execute_action of type action are handled', function (assert) {
|
|
assert.expect(11);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
// click on 'Execute action' button (should execute an action)
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two parts in the breadcrumbs");
|
|
testUtils.dom.click(actionManager.$('.o_form_view button:contains(Execute action)'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 3,
|
|
"the returned action should have been stacked over the previous one");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"the returned action should have been executed");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list for action 3
|
|
'read', // form for action 3
|
|
'/web/action/load', // click on 'Execute action' button
|
|
'load_views',
|
|
'/web/dataset/search_read', // kanban for action 4
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('requests for execute_action of type object: disable buttons', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var self = this;
|
|
var def;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
if (route === '/web/dataset/call_button') {
|
|
return $.when(false);
|
|
} else if (args.method === 'read') {
|
|
// Block the 'read' call
|
|
var result = this._super.apply(this, arguments);
|
|
return $.when(def).then(_.constant(result));
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
// click on 'Call method' button (should call an Object method)
|
|
def = $.Deferred();
|
|
testUtils.dom.click(actionManager.$('.o_form_view button:contains(Call method)'));
|
|
|
|
// Buttons should be disabled
|
|
assert.strictEqual(
|
|
actionManager.$('.o_form_view button:contains(Call method)').attr('disabled'),
|
|
'disabled', 'buttons should be disabled')
|
|
|
|
// Release the 'read' call
|
|
def.resolve();
|
|
|
|
// Buttons should be enabled after the reload
|
|
assert.strictEqual(
|
|
actionManager.$('.o_form_view button:contains(Call method)').attr('disabled'),
|
|
undefined, 'buttons should be disabled')
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('can open different records from a multi record view', function (assert) {
|
|
assert.expect(11);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open the first record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'First record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
assert.strictEqual(actionManager.$('.o_field_widget[name=foo]').text(), 'yop',
|
|
"should have opened the correct record");
|
|
|
|
// go back to list view using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a'));
|
|
|
|
// open the second record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:nth(1)'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Second record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
assert.strictEqual(actionManager.$('.o_field_widget[name=foo]').text(), 'blip',
|
|
"should have opened the correct record");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read', // list
|
|
'read', // form
|
|
'/web/dataset/search_read', // list
|
|
'read', // form
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('restore previous view state when switching back', function (assert) {
|
|
assert.expect(5);
|
|
|
|
this.actions[2].views.unshift([false, 'graph']);
|
|
this.archs['partner,false,graph'] = '<graph></graph>';
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.hasClass($('.o_control_panel .fa-bar-chart-o'),'active',
|
|
"bar chart button is active");
|
|
assert.doesNotHaveClass($('.o_control_panel .fa-area-chart'), 'active',
|
|
"line chart button is not active");
|
|
|
|
// display line chart
|
|
testUtils.dom.click($('.o_control_panel .fa-area-chart'));
|
|
assert.hasClass($('.o_control_panel .fa-area-chart'),'active',
|
|
"line chart button is now active");
|
|
|
|
// switch to kanban and back to graph view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
assert.strictEqual($('.o_control_panel .fa-area-chart').length, 0,
|
|
"graph buttons are no longer in control panel");
|
|
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_graph'));
|
|
assert.hasClass($('.o_control_panel .fa-area-chart'),'active',
|
|
"line chart button is still active");
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('view switcher is properly highlighted in graph view', function (assert) {
|
|
assert.expect(4);
|
|
|
|
// note: this test should be moved to graph tests ?
|
|
|
|
this.actions[2].views.splice(1, 1, [false, 'graph']);
|
|
this.archs['partner,false,graph'] = '<graph></graph>';
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_list'),'active',
|
|
"list button in control panel is active");
|
|
assert.doesNotHaveClass($('.o_control_panel .o_cp_switch_graph'), 'active',
|
|
"graph button in control panel is not active");
|
|
|
|
// switch to graph view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_graph'));
|
|
assert.doesNotHaveClass($('.o_control_panel .o_cp_switch_list'), 'active',
|
|
"list button in control panel is not active");
|
|
assert.hasClass($('.o_control_panel .o_cp_switch_graph'),'active',
|
|
"graph button in control panel is active");
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('can interact with search view', function (assert) {
|
|
assert.expect(2);
|
|
|
|
this.archs['partner,false,search'] = '<search>'+
|
|
'<group>'+
|
|
'<filter name="foo" string="foo" context="{\'group_by\': \'foo\'}"/>' +
|
|
'</group>'+
|
|
'</search>';
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.doesNotHaveClass(actionManager.$('.o_list_view'), 'o_list_view_grouped',
|
|
"list view is not grouped");
|
|
|
|
// open group by dropdown
|
|
testUtils.dom.click($('.o_control_panel .o_cp_right button:contains(Group By)'));
|
|
|
|
// click on first link
|
|
testUtils.dom.click($('.o_control_panel .o_group_by_menu a:first'));
|
|
|
|
assert.hasClass(actionManager.$('.o_list_view'),'o_list_view_grouped',
|
|
'list view is now grouped');
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('can open a many2one external window', function (assert) {
|
|
// AAB: this test could be merged with 'many2ones in form views' in relational_fields_tests.js
|
|
assert.expect(8);
|
|
|
|
this.data.partner.records[0].bar = 2;
|
|
this.archs['partner,false,search'] = '<search>'+
|
|
'<group>'+
|
|
'<filter name="foo" string="foo" context="{\'group_by\': \'foo\'}"/>' +
|
|
'</group>'+
|
|
'</search>';
|
|
this.archs['partner,false,form'] = '<form>' +
|
|
'<group>' +
|
|
'<field name="foo"/>' +
|
|
'<field name="bar"/>' +
|
|
'</group>' +
|
|
'</form>';
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(route);
|
|
if (args.method === "get_formview_id") {
|
|
return $.when(false);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open first record in form view
|
|
testUtils.dom.click(actionManager.$('.o_data_row:first'));
|
|
// click on edit
|
|
testUtils.dom.click($('.o_control_panel .o_form_button_edit'));
|
|
|
|
// click on external button for m2o
|
|
testUtils.dom.click(actionManager.$('.o_external_button'));
|
|
assert.verifySteps([
|
|
'/web/action/load', // initial load action
|
|
'/web/dataset/call_kw/partner', // load views
|
|
'/web/dataset/search_read', // read list view data
|
|
'/web/dataset/call_kw/partner/read', // read form view data
|
|
'/web/dataset/call_kw/partner/get_formview_id', // get form view id
|
|
'/web/dataset/call_kw/partner', // load form view for modal
|
|
'/web/dataset/call_kw/partner/read' // read data for m2o record
|
|
]);
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('ask for confirmation when leaving a "dirty" view', function (assert) {
|
|
assert.expect(4);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(4);
|
|
|
|
// open record in form view
|
|
testUtils.dom.click(actionManager.$('.o_kanban_record:first'));
|
|
|
|
// edit record
|
|
testUtils.dom.click($('.o_control_panel button.o_form_button_edit'));
|
|
testUtils.fields.editInput(actionManager.$('input[name="foo"]'), 'pinkypie');
|
|
|
|
// go back to kanban view
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb-item:first a'));
|
|
|
|
assert.strictEqual($('.modal .modal-body').text(),
|
|
"The record has been modified, your changes will be discarded. Do you want to proceed?",
|
|
"should display a modal dialog to confirm discard action");
|
|
|
|
// cancel
|
|
testUtils.dom.click($('.modal .modal-footer button.btn-secondary'));
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"should still be in form view");
|
|
|
|
// go back again to kanban view
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb-item:first a'));
|
|
|
|
// confirm discard
|
|
testUtils.dom.click($('.modal .modal-footer button.btn-primary'));
|
|
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should no longer be in form view");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should be in kanban view");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('limit set in action is passed to each created controller', function (assert) {
|
|
assert.expect(2);
|
|
|
|
_.findWhere(this.actions, {id: 3}).limit = 2;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsN(actionManager, '.o_data_row', 2,
|
|
"should only display 2 record");
|
|
|
|
// switch to kanban view
|
|
testUtils.dom.click($('.o_control_panel .o_cp_switch_kanban'));
|
|
|
|
assert.strictEqual(actionManager.$('.o_kanban_record:not(.o_kanban_ghost)').length, 2,
|
|
"should only display 2 record");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('go back to a previous action using the breadcrumbs', function (assert) {
|
|
assert.expect(10);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open a record in form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'First record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
// push another action on top of the first one, and come back to the form view
|
|
actionManager.doAction(4);
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 3,
|
|
"there should be three controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Partners Action 4',
|
|
"breadcrumbs should contain the name of the current action");
|
|
// go back using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:nth(1)'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 2,
|
|
"there should be two controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'First record',
|
|
"breadcrumbs should contain the display_name of the opened record");
|
|
|
|
// push again the other action on top of the first one, and come back to the list view
|
|
actionManager.doAction(4);
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 3,
|
|
"there should be three controllers in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Partners Action 4',
|
|
"breadcrumbs should contain the name of the current action");
|
|
// go back using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:first'));
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item').length, 1,
|
|
"there should be one controller in the breadcrumbs");
|
|
assert.strictEqual($('.o_control_panel .breadcrumb-item:last').text(), 'Partners',
|
|
"breadcrumbs should contain the name of the current action");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('form views are restored in readonly when coming back in breadcrumbs', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
// open a record in form view
|
|
actionManager.$('.o_list_view .o_data_row:first').click();
|
|
// switch to edit mode
|
|
$('.o_control_panel .o_form_button_edit').click();
|
|
|
|
assert.ok(actionManager.$('.o_form_view').hasClass('o_form_editable'));
|
|
// do some other action
|
|
actionManager.doAction(4);
|
|
// go back to form view
|
|
$('.o_control_panel .breadcrumb a').last().click();
|
|
assert.ok(actionManager.$('.o_form_view').hasClass('o_form_readonly'));
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('honor group_by specified in actions context', function (assert) {
|
|
assert.expect(5);
|
|
|
|
_.findWhere(this.actions, {id: 3}).context = "{'group_by': 'bar'}";
|
|
this.archs['partner,false,search'] = '<search>'+
|
|
'<group>'+
|
|
'<filter name="foo" string="foo" context="{\'group_by\': \'foo\'}"/>' +
|
|
'</group>'+
|
|
'</search>';
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(3);
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view_grouped',
|
|
"should be grouped");
|
|
assert.containsN(actionManager, '.o_group_header', 2,
|
|
"should be grouped by 'bar' (two groups) at first load");
|
|
|
|
// groupby 'bar' using the searchview
|
|
testUtils.dom.click($('.o_control_panel .o_cp_right button:contains(Group By)'));
|
|
testUtils.dom.click($('.o_control_panel .o_group_by_menu a:first'));
|
|
|
|
assert.containsN(actionManager, '.o_group_header', 5,
|
|
"should be grouped by 'foo' (five groups)");
|
|
|
|
// remove the groupby in the searchview
|
|
testUtils.dom.click($('.o_control_panel .o_searchview .o_facet_remove'));
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view_grouped',
|
|
"should still be grouped");
|
|
assert.containsN(actionManager, '.o_group_header', 2,
|
|
"should be grouped by 'bar' (two groups) at reload");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('switch request to unknown view type', function (assert) {
|
|
assert.expect(7);
|
|
|
|
this.actions.push({
|
|
id: 33,
|
|
name: 'Partners',
|
|
res_model: 'partner',
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'list'], [1, 'kanban']], // no form view
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(33);
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should display the list view");
|
|
|
|
// try to open a record in a form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"should still display the list view");
|
|
assert.containsNone(actionManager, '.o_form_view',
|
|
"should not display the form view");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('save current search', function (assert) {
|
|
assert.expect(4);
|
|
|
|
testUtils.mock.patch(ListController, {
|
|
getContext: function () {
|
|
return {
|
|
shouldBeInFilterContext: true,
|
|
};
|
|
},
|
|
});
|
|
|
|
this.actions.push({
|
|
id: 33,
|
|
context: {
|
|
shouldNotBeInFilterContext: false,
|
|
},
|
|
name: 'Partners',
|
|
res_model: 'partner',
|
|
search_view_id: [1, 'a custom search view'],
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
intercepts: {
|
|
create_filter: function (event) {
|
|
var filter = event.data.filter;
|
|
assert.deepEqual(filter.domain, "[('bar', '=', 1)]",
|
|
"should save the correct domain");
|
|
assert.deepEqual(filter.context, {shouldBeInFilterContext: true},
|
|
"should save the correct context");
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(33);
|
|
|
|
assert.containsN(actionManager, '.o_data_row', 5,
|
|
"should contain 5 records");
|
|
|
|
// filter on bar
|
|
testUtils.dom.click($('.o_control_panel .o_search_options .o_dropdown_toggler_btn:contains(Filters)'));
|
|
testUtils.dom.click($('.o_control_panel .o_filters_menu a:contains(Bar)'));
|
|
|
|
assert.containsN(actionManager, '.o_data_row', 2);
|
|
|
|
// save filter
|
|
testUtils.dom.click($('.o_control_panel .o_search_options .o_dropdown_toggler_btn:contains(Favorites)'));
|
|
testUtils.dom.click($('.o_control_panel .o_save_search'));
|
|
$('.o_control_panel .o_save_name input[type=text]').val('some name'); // name the filter
|
|
testUtils.dom.click($('.o_control_panel .o_save_name button'));
|
|
|
|
testUtils.mock.unpatch(ListController);
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test("search menus are still available when switching between actions", function (assert) {
|
|
assert.expect(3);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
actionManager.doAction(1);
|
|
assert.strictEqual($('.o_search_options .o_dropdown:visible .o_filters_menu').length, 1,
|
|
"the search options should be available");
|
|
|
|
actionManager.doAction(3);
|
|
assert.strictEqual($('.o_search_options .o_dropdown:visible .o_filters_menu').length, 1,
|
|
"the search options should be available");
|
|
|
|
// go back using the breadcrumbs
|
|
testUtils.dom.click($('.o_control_panel .breadcrumb a:first'));
|
|
assert.strictEqual($('.o_search_options .o_dropdown:visible .o_filters_menu').length, 1,
|
|
"the search options should be available");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test("doAction with option 'keepSearchView'", function (assert) {
|
|
assert.expect(4);
|
|
|
|
this.actions.push({
|
|
id: 33,
|
|
name: 'Partners',
|
|
res_model: 'partner',
|
|
search_view_id: [1, 'a specific search view'],
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
var checkRPC = false;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
if (checkRPC && route === '/web/dataset/search_read') {
|
|
assert.deepEqual(args.domain, [['bar', '=', 1]],
|
|
"should search with the correct domain");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
actionManager.doAction(33);
|
|
|
|
checkRPC = true;
|
|
testUtils.dom.click($('.o_control_panel .o_search_options .o_dropdown_toggler_btn:contains(Filters)'));
|
|
testUtils.dom.click($('.o_control_panel .o_filters_menu a:contains(Bar)'));
|
|
assert.strictEqual($('.o_control_panel .o_facet_values').text().trim(), 'Bar',
|
|
"the filter on Bar should appear in the search view");
|
|
|
|
actionManager.doAction(3, {keepSearchView: true});
|
|
assert.strictEqual($('.o_control_panel .o_facet_values').text().trim(), 'Bar',
|
|
"the filter on Bar should still be in the search view");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test("current act_window action is stored in session_storage", function (assert) {
|
|
assert.expect(1);
|
|
|
|
var expectedAction = _.extend({}, _.findWhere(this.actions, {id: 3}), {
|
|
context: {},
|
|
});
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
session_storage: SessionStorageService.extend({
|
|
setItem: function (key, value) {
|
|
assert.strictEqual(value, JSON.stringify(expectedAction),
|
|
"should store the executed action in the sessionStorage");
|
|
},
|
|
}),
|
|
},
|
|
});
|
|
|
|
actionManager.doAction(3);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test("store evaluated context of current action in session_storage", function (assert) {
|
|
// this test ensures that we don't store stringified instances of
|
|
// CompoundContext in the session_storage, as they would be meaningless
|
|
// once restored
|
|
assert.expect(1);
|
|
|
|
var expectedAction = _.extend({}, _.findWhere(this.actions, {id: 4}), {
|
|
context: {
|
|
active_model: 'partner',
|
|
active_id: 1,
|
|
active_ids: [1],
|
|
},
|
|
});
|
|
var checkSessionStorage = false;
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
services: {
|
|
session_storage: SessionStorageService.extend({
|
|
setItem: function (key, value) {
|
|
if (checkSessionStorage) {
|
|
assert.strictEqual(value, JSON.stringify(expectedAction),
|
|
"should correctly store the executed action in the sessionStorage");
|
|
}
|
|
},
|
|
}),
|
|
},
|
|
});
|
|
|
|
// execute an action and open a record in form view
|
|
actionManager.doAction(3);
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
// click on 'Execute action' button (it executes an action with a CompoundContext as context)
|
|
checkSessionStorage = true;
|
|
testUtils.dom.click(actionManager.$('.o_form_view button:contains(Execute action)'));
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Actions in target="new"');
|
|
|
|
QUnit.test('can execute act_window actions in target="new"', function (assert) {
|
|
assert.expect(7);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(5);
|
|
|
|
assert.strictEqual($('.o_technical_modal .o_form_view').length, 1,
|
|
"should have rendered a form view in a modal");
|
|
assert.hasClass($('.o_technical_modal .modal-body'),'o_act_window',
|
|
"dialog main element should have classname 'o_act_window'");
|
|
assert.hasClass($('.o_technical_modal .o_form_view'),'o_form_editable',
|
|
"form view should be in edit mode");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'default_get',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('chained action on_close', function (assert) {
|
|
assert.expect(3);
|
|
|
|
function on_close() {
|
|
assert.step('Close Action');
|
|
};
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(5, {on_close: on_close});
|
|
|
|
// a target=new action shouldn't activate the on_close
|
|
actionManager.doAction(5);
|
|
assert.verifySteps([]);
|
|
|
|
// An act_window_close should trigger the on_close
|
|
actionManager.doAction(10);
|
|
assert.verifySteps(['Close Action']);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('footer buttons are moved to the dialog footer', function (assert) {
|
|
assert.expect(3);
|
|
|
|
this.archs['partner,false,form'] = '<form>' +
|
|
'<field name="display_name"/>' +
|
|
'<footer>' +
|
|
'<button string="Create" type="object" class="infooter"/>' +
|
|
'</footer>' +
|
|
'</form>';
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction(5);
|
|
|
|
assert.strictEqual($('.o_technical_modal .modal-body button.infooter').length, 0,
|
|
"the button should not be in the body");
|
|
assert.strictEqual($('.o_technical_modal .modal-footer button.infooter').length, 1,
|
|
"the button should be in the footer");
|
|
assert.strictEqual($('.o_technical_modal .modal-footer button').length, 1,
|
|
"the modal footer should only contain one button");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('on_attach_callback is called for actions in target="new"', function (assert) {
|
|
assert.expect(4);
|
|
|
|
var ClientAction = AbstractAction.extend({
|
|
className: 'o_test',
|
|
on_attach_callback: function () {
|
|
assert.step('on_attach_callback');
|
|
assert.ok(actionManager.currentDialogController,
|
|
"the currentDialogController should have been set already");
|
|
},
|
|
});
|
|
core.action_registry.add('test', ClientAction);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction({
|
|
tag: 'test',
|
|
target: 'new',
|
|
type: 'ir.actions.client',
|
|
});
|
|
|
|
assert.strictEqual($('.modal .o_test').length, 1,
|
|
"should have rendered the client action in a dialog");
|
|
assert.verifySteps(['on_attach_callback']);
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.test;
|
|
});
|
|
|
|
QUnit.module('Actions in target="inline"');
|
|
|
|
QUnit.test('form views for actions in target="inline" open in edit mode', function (assert) {
|
|
assert.expect(5);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
actionManager.doAction(6);
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view.o_form_editable',
|
|
"should have rendered a form view in edit mode");
|
|
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'read',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Actions in target="fullscreen"');
|
|
|
|
QUnit.test('correctly execute act_window actions in target="fullscreen"', function (assert) {
|
|
assert.expect(7);
|
|
|
|
this.actions[0].target = 'fullscreen';
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
assert.step(args.method || route);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
intercepts: {
|
|
toggle_fullscreen: function () {
|
|
assert.step('toggle_fullscreen');
|
|
},
|
|
},
|
|
});
|
|
actionManager.doAction(1);
|
|
|
|
assert.strictEqual($('.o_control_panel').length, 1,
|
|
"should have rendered a control panel");
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
"should have rendered a kanban view");
|
|
assert.verifySteps([
|
|
'/web/action/load',
|
|
'load_views',
|
|
'/web/dataset/search_read',
|
|
'toggle_fullscreen',
|
|
]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('"ir.actions.act_window_close" actions');
|
|
|
|
QUnit.test('close the currently opened dialog', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
// execute an action in target="new"
|
|
actionManager.doAction(5);
|
|
assert.strictEqual($('.o_technical_modal .o_form_view').length, 1,
|
|
"should have rendered a form view in a modal");
|
|
|
|
// execute an 'ir.actions.act_window_close' action
|
|
actionManager.doAction({
|
|
type: 'ir.actions.act_window_close',
|
|
});
|
|
assert.strictEqual($('.o_technical_modal').length, 0,
|
|
"should have closed the modal");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('execute "on_close" only if there is no dialog to close', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
// execute an action in target="new"
|
|
actionManager.doAction(5);
|
|
|
|
var options = {
|
|
on_close: assert.step.bind(assert, 'on_close'),
|
|
};
|
|
// execute an 'ir.actions.act_window_close' action
|
|
// should not call 'on_close' as there is a dialog to close
|
|
actionManager.doAction({type: 'ir.actions.act_window_close'}, options);
|
|
|
|
assert.verifySteps([]);
|
|
|
|
// execute again an 'ir.actions.act_window_close' action
|
|
// should call 'on_close' as there is no dialog to close
|
|
actionManager.doAction({type: 'ir.actions.act_window_close'}, options);
|
|
|
|
assert.verifySteps(['on_close']);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('doAction resolved with an action', function (assert) {
|
|
assert.expect(4);
|
|
|
|
this.actions.push({
|
|
id: 21,
|
|
name: 'A Close Action',
|
|
type: 'ir.actions.act_window_close',
|
|
});
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
actionManager.doAction(21).then(function (action) {
|
|
assert.ok(action, "doAction should be resolved with an action");
|
|
assert.strictEqual(action.id, 21,
|
|
"should be resolved with correct action id");
|
|
assert.strictEqual(action.name, 'A Close Action',
|
|
"should be resolved with correct action name");
|
|
assert.strictEqual(action.type, 'ir.actions.act_window_close',
|
|
"should be resolved with correct action type");
|
|
actionManager.destroy();
|
|
});
|
|
});
|
|
|
|
QUnit.test('close action with provided infos', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
var options = {
|
|
on_close: function (infos) {
|
|
assert.strictEqual(infos, 'just for testing',
|
|
"should have the correct close infos");
|
|
}
|
|
};
|
|
|
|
actionManager.doAction({
|
|
type: 'ir.actions.act_window_close',
|
|
infos: 'just for testing',
|
|
}, options);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('history back calls on_close handler of dialog action', function (assert) {
|
|
assert.expect(2);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
|
|
// open a new dialog form
|
|
actionManager.doAction(this.actions[4], {
|
|
on_close: function () {
|
|
assert.step('on_close');
|
|
},
|
|
});
|
|
|
|
actionManager.trigger_up('history_back');
|
|
assert.verifySteps(['on_close'], "should have called the on_close handler");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.test('properly drop client actions after new action is initiated', function (assert) {
|
|
assert.expect(1);
|
|
|
|
var slowWillStartDef = $.Deferred();
|
|
|
|
var ClientAction = AbstractAction.extend({
|
|
willStart: function () {
|
|
return slowWillStartDef;
|
|
},
|
|
});
|
|
|
|
core.action_registry.add('slowAction', ClientAction);
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction('slowAction');
|
|
actionManager.doAction(4);
|
|
slowWillStartDef.resolve();
|
|
assert.containsOnce(actionManager, '.o_kanban_view',
|
|
'should have loaded a kanban view');
|
|
|
|
actionManager.destroy();
|
|
delete core.action_registry.map.slowAction;
|
|
});
|
|
|
|
|
|
QUnit.test('abstract action does not crash on navigation_moves', function (assert) {
|
|
assert.expect(1);
|
|
var ClientAction = AbstractAction.extend({
|
|
});
|
|
core.action_registry.add('ClientAction', ClientAction);
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction('ClientAction');
|
|
actionManager.trigger_up('navigation_move', {direction:'down'});
|
|
|
|
assert.ok(true); // no error so it's good
|
|
actionManager.destroy();
|
|
delete core.action_registry.ClientAction;
|
|
});
|
|
|
|
QUnit.test('fields in abstract action does not crash on navigation_moves', function (assert) {
|
|
assert.expect(1);
|
|
var self = this;
|
|
|
|
// create a client action with 2 input field
|
|
var inputWidget;
|
|
var secondInputWidget;
|
|
var ClientAction = AbstractAction.extend(StandaloneFieldManagerMixin, {
|
|
init: function () {
|
|
this._super.apply(this, arguments);
|
|
StandaloneFieldManagerMixin.init.call(this);
|
|
},
|
|
start: function () {
|
|
var _self = this;
|
|
|
|
return this.model.makeRecord('partner', [{
|
|
name: 'display_name',
|
|
type: 'char',
|
|
}]).then(function (recordID) {
|
|
var record = _self.model.get(recordID);
|
|
inputWidget = new BasicFields.InputField(_self, 'display_name', record, {mode: 'edit',});
|
|
_self._registerWidget(recordID, 'display_name', inputWidget);
|
|
|
|
secondInputWidget = new BasicFields.InputField(_self, 'display_name', record, {mode: 'edit',});
|
|
secondInputWidget.attrs = {className:"secondField"};
|
|
_self._registerWidget(recordID, 'display_name', secondInputWidget);
|
|
|
|
inputWidget.appendTo(_self.$el);
|
|
secondInputWidget.appendTo(_self.$el);
|
|
});
|
|
}
|
|
});
|
|
core.action_registry.add('ClientAction', ClientAction);
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
});
|
|
actionManager.doAction('ClientAction');
|
|
inputWidget.$el[0].focus();
|
|
var event = $.Event('keydown', {
|
|
which: $.ui.keyCode.TAB,
|
|
keyCode: $.ui.keyCode.TAB,
|
|
});
|
|
$(inputWidget.$el[0]).trigger(event);
|
|
|
|
assert.notOk(event.isDefaultPrevented(),
|
|
"the keyboard event default should not be prevented"); // no crash is good
|
|
actionManager.destroy();
|
|
delete core.action_registry.ClientAction;
|
|
});
|
|
|
|
QUnit.test('web client is not deadlocked when a view crashes', function (assert) {
|
|
assert.expect(3);
|
|
|
|
var readOnFirstRecordDef = $.Deferred();
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'read' && args.args[0][0] === 1) {
|
|
return readOnFirstRecordDef;
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
}
|
|
});
|
|
|
|
actionManager.doAction(3);
|
|
|
|
// open first record in form view. this will crash and will not
|
|
// display a form view
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first'));
|
|
|
|
readOnFirstRecordDef.reject("not working as intended");
|
|
|
|
assert.containsOnce(actionManager, '.o_list_view',
|
|
"there should still be a list view in dom");
|
|
|
|
// open another record, the read will not crash
|
|
testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:eq(2)'));
|
|
|
|
assert.containsNone(actionManager, '.o_list_view',
|
|
"there should not be a list view in dom");
|
|
|
|
assert.containsOnce(actionManager, '.o_form_view',
|
|
"there should be a form view in dom");
|
|
|
|
actionManager.destroy();
|
|
});
|
|
|
|
QUnit.module('Search View Action');
|
|
|
|
QUnit.test('search view should keep focus during do_search', function (assert) {
|
|
assert.expect(5);
|
|
|
|
/* One should be able to type something in the search view, press on enter to
|
|
* make the facet and trigger the search, then do this process
|
|
* over and over again seamlessly.
|
|
* Verifying the input's value is a lot trickier than verifying the search_read
|
|
* because of how native events are handled in tests
|
|
*/
|
|
|
|
var searchDeferred = $.Deferred();
|
|
|
|
var actionManager = createActionManager({
|
|
actions: this.actions,
|
|
archs: this.archs,
|
|
data: this.data,
|
|
mockRPC: function (route, args) {
|
|
if (route === '/web/dataset/search_read') {
|
|
assert.step('search_read ' + args.domain);
|
|
if ( _.isEqual(args.domain, [['foo', 'ilike', 'm']])) {
|
|
return searchDeferred.then(this._super.bind(this, route, args));
|
|
}
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
}
|
|
});
|
|
|
|
actionManager.doAction(3);
|
|
|
|
var $searchInput = $('.o_searchview input');
|
|
$searchInput.trigger($.Event('keypress', {key: 'm', which: 109, keyCode: 109}));
|
|
$searchInput.trigger($.Event('keydown', {key: 'Enter', which: 13, keyCode: 13}));
|
|
|
|
assert.verifySteps(["search_read ",
|
|
"search_read foo,ilike,m"]);
|
|
|
|
// Triggering the do_search above will kill the current searchview Input
|
|
$searchInput = $('.o_searchview input');
|
|
$searchInput.trigger($.Event('keypress', {key: 'o', which: 111, keyCode: 111}));
|
|
|
|
// We have something in the input of the search view. Making the search_read
|
|
// return at this point will trigger the redraw of the view.
|
|
// However we want to hold on to what we just typed
|
|
searchDeferred.resolve();
|
|
|
|
$searchInput.trigger($.Event('keydown', {key: 'Enter', which: 13, keyCode: 13}));
|
|
|
|
assert.verifySteps(["search_read ",
|
|
"search_read foo,ilike,m",
|
|
"search_read |,foo,ilike,m,foo,ilike,o"]);
|
|
|
|
actionManager.destroy();
|
|
});
|
|
});
|
|
|
|
});
|