[MERGE] web,*: update code base to jQuery 3.*
The goal of this task is to update jQuery from version 1.11 to 3.x. Task-id : 1896658 As we are skipping 2 magor versions, there are some breaking changes in the jquery library described here: https://jquery.com/upgrade-guide/3.0/ Main changes are: ------------------------- 1. Update the libraries jQuery, jQueryUI, QUnit and select2 to their latest version. The link to generate the build of jQueryUI with the used components: [jquery UI build](https://jqueryui.com/download/#!version=1.12.1&components=111110010101111110100010110000011010000000000000) 2. Use native promises ([a complete explanation about promises here](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md)) everywhere possible: In order to [be A+ compliant](https://promisesaplus.com/), the jq3 deferred uses `setTimeout(..., 0)` for every then and catch, which takes between 4 and 10 ms by call. This would be way too slow to use in Odoo. To prevent this, we have used the native Promises everywhere possible. Be it with jq deferred or native promises, all promises are now asynchronous, this example will ALWAYS result in printing first "just after blabla" then "blabla" because the `then` function will always be asynchronous (opposite to what jquery used to do) ``` function () { Promise.resolve("blabla").then(console.log); console.log("just after blabla"); } // console: // just after blabla // blabla ``` 3. On native promises, you should see the catch like a try/catch in javascript, meaning that it will catch all errors including coding errors: For this we have introduced the concept of `guardedCatch`, which will be executed on promise rejection unless the promise is rejected because there is a code error or runtime error (calls on undefined, etc). 4. Native Promises do not have `done`, `fail` and `always` methods nor does it have the `state` method - `fail` can be replaced with `guardedCatch` - `return $.when(x).done(function() ...)` can be replaced with ``` var x = Promise.resolve(); // we get the x promise in a local variable, without then x.then(function() ...); // we add a new handler to x. This operation returns a new promise we don't care about return x; // we return the x promise that will be completed without waiting for the then ``` - `$.when().done(function ...)` without the return can be replaced by `then(function ....)`, because if it is not returned, it doesn't matter what is chained or not `$.when().always(function() ...)` can be replaced with ``` var always = function() ... Promise.resolve().then(always).guardedCatch(always); ``` - It is not possible to inspect the state of a promise, except in debug tools of chrome and firefox. Any code that relies on `$.Deferred.state()` should be rewritten to use flags in `then` and `catch` handlers. Remember that it is not possible to execute the handlers in a synchroneous manner, so when a promise is created, its `then` will never be called on the same execution stack. At best it will be called on the next microtask tick (before the next setTimeout). 5. We have introduced the concept of guardedCatch, to copy the behavior of the old fail: The guarded catch will only execute the handler in parameter if the rejection reason is not an `Error` ``` var failingPromise = Promise.reject({message:"I am the rejection reason"}); failingPromise.guardedCatch(function(reason) {console.log(reason);}); // is called, then returns a rejected promise failingPromise.catch(function(reason) {console.log(reason);}); // is called ``` will write the reason "I am the rejection reason" to the console twice: once because the `guardedCatch` will execute (and return a rejected `Promise` with the same reason it was called), a second time because the `catch` will catch the rejected reason returned by the `guardedCatch` ``` var promiseWithError = Promise.resolve().then(function() { a.b = 0; // <-- the variable a does not exist, so a.b is an error }); promiseWithError.guardedCatch(function(reason) {console.log(reason);}); // will not be called promiseWithError.catch(function(reason) {console.log(reason);}); // will be called ``` contrary to how deferred used to work, here the catch will be called because it behaves like a `try... catch...` and will catch the coding error like if it was a business error. The `guardedCatch` however does not call the handler if the rejection reason is an error. In all Odoo code, we never reject a promise with an error and always use objects to defined the rejected reason, so it is a good idea to use `guardedCatch` everywhere. 6. Native Promises can have only one parameter passed to a `then` function or a `catch` function: `xxx.then(function(param1, param2) {...}); // param2 will never have a value` - we can replace `$.when(prom1, prom2).then(function(result1, result2) { ... });` by ``` Promise.all([prom1, prom2]).then(function(results) { var result1 = results[1]; var result2 = results[2]; ... }); ``` 7. We are now using ES7 code in the unit tests (async await). closes odoo/odoo#28457 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Co-authored-by: Aaron Bohy <aab@odoo.com> Co-authored-by: Christophe Matthieu <chm@odoo.com> Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com> Co-authored-by: David Monjoie <dmo@odoo.com> Co-authored-by: Martin Geubelle <mge@odoo.com> Co-authored-by: svs-odoo <svs@odoo.com> Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
This commit is contained in:
co-authored by
Aaron Bohy
Christophe Matthieu
Mathieu Duckerts-Antoine
David Monjoie
Martin Geubelle
svs-odoo
Vincent Schippefilt
commit
fc60c49ba3
@@ -105,7 +105,7 @@ var StatementAction = AbstractAction.extend({
|
||||
var self = this;
|
||||
_.each(this.widgets, function(widget) {
|
||||
widget.destroy();
|
||||
})
|
||||
});
|
||||
this.widgets = [];
|
||||
this.model.reload()
|
||||
.then(function() {
|
||||
@@ -122,23 +122,26 @@ var StatementAction = AbstractAction.extend({
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
var args = arguments;
|
||||
var sup = this._super;
|
||||
|
||||
this.renderer.prependTo(self.$('.o_form_sheet'));
|
||||
this._renderLines();
|
||||
return this.renderer.prependTo(self.$('.o_form_sheet')).then(function() {
|
||||
return self._renderLines().then(function() {
|
||||
// No more lines to reconcile, trigger the rainbowman.
|
||||
var initialState = self.renderer._initialState;
|
||||
if(initialState.valuenow === initialState.valuemax){
|
||||
initialState.context = self.model.getContext();
|
||||
self.renderer.showRainbowMan(initialState);
|
||||
}else{
|
||||
// Create a notification if some lines has been reconciled automatically.
|
||||
if(initialState.valuenow > 0)
|
||||
self.renderer._renderNotifications(self.model.statement.notifications);
|
||||
self._openFirstLine();
|
||||
}
|
||||
|
||||
// No more lines to reconcile, trigger the rainbowman.
|
||||
var initialState = this.renderer._initialState;
|
||||
if(initialState.valuenow === initialState.valuemax){
|
||||
initialState.context = this.model.getContext();
|
||||
this.renderer.showRainbowMan(initialState);
|
||||
}else{
|
||||
// Create a notification if some lines has been reconciled automatically.
|
||||
if(initialState.valuenow > 0)
|
||||
this.renderer._renderNotifications(this.model.statement.notifications);
|
||||
this._openFirstLine();
|
||||
}
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
return sup.apply(self, args);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -176,7 +179,7 @@ var StatementAction = AbstractAction.extend({
|
||||
_loadMore: function(qty) {
|
||||
var self = this;
|
||||
return this.model.loadMore(qty).then(function () {
|
||||
self._renderLines();
|
||||
return self._renderLines();
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -192,7 +195,9 @@ var StatementAction = AbstractAction.extend({
|
||||
}))[0];
|
||||
if (handle) {
|
||||
var line = this.model.getLine(handle);
|
||||
this.model.changeMode(handle, 'match').always(function () {
|
||||
this.model.changeMode(handle, 'match').then(function () {
|
||||
self._getWidget(handle).update(line);
|
||||
}).guardedCatch(function(){
|
||||
self._getWidget(handle).update(line);
|
||||
});
|
||||
}
|
||||
@@ -206,11 +211,12 @@ var StatementAction = AbstractAction.extend({
|
||||
_renderLines: function () {
|
||||
var self = this;
|
||||
var linesToDisplay = this.model.getStatementLines();
|
||||
var linePromises = [];
|
||||
_.each(linesToDisplay, function (line, handle) {
|
||||
var widget = new self.config.LineRenderer(self, self.model, line);
|
||||
widget.handle = handle;
|
||||
self.widgets.push(widget);
|
||||
widget.appendTo(self.$('.o_reconciliation_lines'));
|
||||
linePromises.push(widget.appendTo(self.$('.o_reconciliation_lines')));
|
||||
});
|
||||
if (this.model.hasMoreLines() === false) {
|
||||
this.renderer.hideLoadMoreButton(true);
|
||||
@@ -218,6 +224,7 @@ var StatementAction = AbstractAction.extend({
|
||||
else {
|
||||
this.renderer.hideLoadMoreButton(false);
|
||||
}
|
||||
return Promise.all(linePromises);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -237,7 +244,7 @@ var StatementAction = AbstractAction.extend({
|
||||
var handle = event.target.handle;
|
||||
var line = this.model.getLine(handle);
|
||||
var mode = line.mode;
|
||||
this.model[_.str.camelize(event.name)](handle, event.data.data).always(function () {
|
||||
this.model[_.str.camelize(event.name)](handle, event.data.data).then(function () {
|
||||
self._getWidget(handle).update(line);
|
||||
if (mode === 'inactive' && line.mode !== 'inactive') {
|
||||
_.each(self.model.lines, function (line, _handle) {
|
||||
@@ -320,7 +327,7 @@ var StatementAction = AbstractAction.extend({
|
||||
},
|
||||
/**
|
||||
* call 'validate' model method then destroy the
|
||||
* validated lines and update the action renderer with the new status bar
|
||||
* validated lines and update the action renderer with the new status bar
|
||||
* values and notifications then open the first available line
|
||||
*
|
||||
* @private
|
||||
@@ -329,8 +336,7 @@ var StatementAction = AbstractAction.extend({
|
||||
_onValidate: function (event) {
|
||||
var self = this;
|
||||
var handle = event.target.handle;
|
||||
var method = 'validate';
|
||||
this.model[method](handle).then(function (result) {
|
||||
this.model.validate(handle).then(function (result) {
|
||||
self.renderer.update({
|
||||
'valuenow': self.model.valuenow,
|
||||
'valuemax': self.model.valuemax,
|
||||
@@ -345,7 +351,7 @@ var StatementAction = AbstractAction.extend({
|
||||
self.widgets.splice(index, 1);
|
||||
});
|
||||
// Get number of widget and if less than constant and if there are more to laod, load until constant
|
||||
if (self.widgets.length < self.model.defaultDisplayQty
|
||||
if (self.widgets.length < self.model.defaultDisplayQty
|
||||
&& self.model.valuemax - self.model.valuenow >= self.model.defaultDisplayQty) {
|
||||
var toLoad = self.model.defaultDisplayQty - self.widgets.length;
|
||||
self._loadMore(toLoad);
|
||||
|
||||
@@ -125,7 +125,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {number} mv_line_id
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
addProposition: function (handle, mv_line_id) {
|
||||
var self = this;
|
||||
@@ -138,19 +138,19 @@ var StatementModel = BasicModel.extend({
|
||||
if(!line.st_line.partner_id && line.reconciliation_proposition
|
||||
&& line.reconciliation_proposition.length == 1 && prop.partner_id && line.type === undefined){
|
||||
return this.changePartner(handle, {'id': prop.partner_id, 'display_name': prop.partner_name}, true)
|
||||
.then(function (result) {
|
||||
return $.when(self._computeLine(line), self._performMoveLine(handle));
|
||||
.then(function () {
|
||||
return Promise.all([self._computeLine(line), self._performMoveLine(handle)]);
|
||||
});
|
||||
}
|
||||
|
||||
return $.when(this._computeLine(line), this._performMoveLine(handle));
|
||||
return Promise.all([this._computeLine(line), this._performMoveLine(handle)]);
|
||||
},
|
||||
/**
|
||||
* change the filter for the target line and fetch the new matched lines
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {string} filter
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
changeFilter: function (handle, filter) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -171,7 +171,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {'inactive' | 'match' | 'create'} mode
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
changeMode: function (handle, mode) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -189,13 +189,13 @@ var StatementModel = BasicModel.extend({
|
||||
if (line.mode === 'create') {
|
||||
return this.createProposition(handle);
|
||||
}
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* call 'write' method on the 'account.bank.statement'
|
||||
*
|
||||
* @param {string} name
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
changeName: function (name) {
|
||||
return this._rpc({
|
||||
@@ -209,7 +209,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {number} offset
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
changeOffset: function (handle, offset) {
|
||||
this.getLine(handle).offset += (offset > 0 ? 1 : -1) * this.limitMoveLines;
|
||||
@@ -223,14 +223,14 @@ var StatementModel = BasicModel.extend({
|
||||
* @param {Object} partner
|
||||
* @param {string} partner.display_name
|
||||
* @param {number} partner.id
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
changePartner: function (handle, partner, preserveMode) {
|
||||
var self = this;
|
||||
var line = this.getLine(handle);
|
||||
line.st_line.partner_id = partner && partner.id;
|
||||
line.st_line.partner_name = partner && partner.display_name || '';
|
||||
return $.when(partner && this._changePartner(handle, partner.id))
|
||||
return Promise.resolve(partner && this._changePartner(handle, partner.id))
|
||||
.then(function() {
|
||||
if(line.st_line.partner_id){
|
||||
_.each(line.reconciliation_proposition, function(prop){
|
||||
@@ -238,7 +238,7 @@ var StatementModel = BasicModel.extend({
|
||||
line.reconciliation_proposition = [];
|
||||
return false;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
return self._computeLine(line);
|
||||
})
|
||||
@@ -249,12 +249,12 @@ var StatementModel = BasicModel.extend({
|
||||
})
|
||||
.then(function () {
|
||||
return line.mode === 'create' ? self.createProposition(handle) : false;
|
||||
})
|
||||
});
|
||||
|
||||
},
|
||||
/**
|
||||
* close the statement
|
||||
* @returns {Deferred<number>} resolves to the res_id of the closed statements
|
||||
* @returns {Promise<number>} resolves to the res_id of the closed statements
|
||||
*/
|
||||
closeStatement: function () {
|
||||
var self = this;
|
||||
@@ -272,14 +272,14 @@ var StatementModel = BasicModel.extend({
|
||||
* then open the first available line
|
||||
*
|
||||
* @param {string} handle
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createProposition: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
var prop = _.filter(line.reconciliation_proposition, '__focus');
|
||||
var last = prop[prop.length-1];
|
||||
if (last && !this._isValid(last)) {
|
||||
return $.Deferred().resolve(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
prop = this._formatQuickCreate(line);
|
||||
@@ -301,7 +301,7 @@ var StatementModel = BasicModel.extend({
|
||||
*/
|
||||
getStatementLines: function () {
|
||||
var self = this;
|
||||
var linesToDisplay = _.pick(this.lines, function(value, key, object) {
|
||||
var linesToDisplay = _.pick(this.lines, function(value, key, object) {
|
||||
if (value.visible === true && self.alreadyDisplayed.indexOf(key) === -1) {
|
||||
self.alreadyDisplayed.push(key);
|
||||
return object;
|
||||
@@ -315,7 +315,6 @@ var StatementModel = BasicModel.extend({
|
||||
* @returns {boolean} true if load more button needs to be displayed
|
||||
*/
|
||||
hasMoreLines: function () {
|
||||
var self = this;
|
||||
var notDisplayed = _.filter(this.lines, function(line) { return !line.visible; });
|
||||
if (notDisplayed.length > 0) {
|
||||
return true;
|
||||
@@ -341,23 +340,23 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @param {Object} context
|
||||
* @param {number[]} context.statement_line_ids
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
load: function (context) {
|
||||
var self = this;
|
||||
this.statement_line_ids = context.statement_line_ids;
|
||||
if (!this.statement_line_ids) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.context = context;
|
||||
return self.reload();
|
||||
|
||||
|
||||
},
|
||||
/**
|
||||
* Load more bank statement line
|
||||
*
|
||||
* @param {integer} qty quantity to load
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loadMore: function(qty) {
|
||||
if (qty === undefined) {
|
||||
@@ -370,10 +369,10 @@ var StatementModel = BasicModel.extend({
|
||||
},
|
||||
/**
|
||||
* RPC method to load informations on lines
|
||||
*
|
||||
*
|
||||
* @param {Array} ids ids of bank statement line passed to rpc call
|
||||
* @param {Array} excluded_ids list of move_line ids that needs to be excluded from search
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loadData: function(ids, excluded_ids) {
|
||||
var self = this;
|
||||
@@ -393,7 +392,8 @@ var StatementModel = BasicModel.extend({
|
||||
self.alreadyDisplayed = [];
|
||||
self.lines = {};
|
||||
self.pagerIndex = 0;
|
||||
self.search_str = $('.reconciliation_search_input').val()
|
||||
// FIXME: model should not be tied to the DOM !
|
||||
self.search_str = $('.reconciliation_search_input').val();
|
||||
var def_statement = this._rpc({
|
||||
model: 'account.reconciliation.widget',
|
||||
method: 'get_bank_statement_data',
|
||||
@@ -446,7 +446,7 @@ var StatementModel = BasicModel.extend({
|
||||
self.accounts = _.object(_.pluck(accounts, 'id'), _.pluck(accounts, 'code'));
|
||||
});
|
||||
var def_taxes = self._loadTaxes();
|
||||
return $.when(def_statement, def_reconcileModel, def_account, def_taxes).then(function () {
|
||||
return Promise.all([def_statement, def_reconcileModel, def_account, def_taxes]).then(function () {
|
||||
_.each(self.lines, function (line) {
|
||||
line.reconcileModels = self.reconcileModels;
|
||||
});
|
||||
@@ -469,21 +469,21 @@ var StatementModel = BasicModel.extend({
|
||||
self.taxes[tax.id] = {
|
||||
price_include: tax.price_include,
|
||||
amount_type: tax.amount_type,
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Add lines into the propositions from the reconcile model
|
||||
* Can add 2 lines, and each with its taxes. The second line become editable
|
||||
* in the create mode.
|
||||
*
|
||||
*
|
||||
* @see 'updateProposition' method for more informations about the
|
||||
* 'amount_type'
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {integer} reconcileModelId
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
quickCreateProposition: function (handle, reconcileModelId) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -493,12 +493,12 @@ var StatementModel = BasicModel.extend({
|
||||
|
||||
var focus = this._formatQuickCreate(line, _.pick(reconcileModel, fields));
|
||||
focus.reconcileModelId = reconcileModelId;
|
||||
if (!line.reconciliation_proposition.every(function(prop) {return prop.to_check == focus.to_check})) {
|
||||
if (!line.reconciliation_proposition.every(function(prop) {return prop.to_check == focus.to_check;})) {
|
||||
new CrashManager().show_warning({data: {
|
||||
exception_type: _t("Incorrect Operation"),
|
||||
message: _t("You cannot mix items with and without the 'To Check' checkbox ticked.")
|
||||
}});
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
line.reconciliation_proposition.push(focus);
|
||||
|
||||
@@ -520,7 +520,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {number} id (move line id)
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
removeProposition: function (handle, id) {
|
||||
var self = this;
|
||||
@@ -541,15 +541,15 @@ var StatementModel = BasicModel.extend({
|
||||
line.mode = (id || line.mode !== "create") && isNaN(id) && !this.avoidCreate ? 'create' : 'match';
|
||||
defs.push(this._computeLine(line));
|
||||
if (line.mode === 'create') {
|
||||
return $.when.apply($, defs).then(function () {
|
||||
return Promise.all(defs).then(function () {
|
||||
return self.createProposition(handle);
|
||||
});
|
||||
} else if (line.mode === 'match') {
|
||||
return $.when.apply($, defs).then(function () {
|
||||
return Promise.all(defs).then(function () {
|
||||
return self._performMoveLine(handle);
|
||||
});
|
||||
}
|
||||
return $.when.apply($, defs);
|
||||
return Promise.all(defs);
|
||||
},
|
||||
getPartialReconcileAmount: function(handle, data) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -562,7 +562,7 @@ var StatementModel = BasicModel.extend({
|
||||
return Math.abs(amount);
|
||||
}
|
||||
if (partial <= Math.abs(prop.amount) && partial >= 0) {
|
||||
return partial
|
||||
return partial;
|
||||
}
|
||||
return Math.abs(amount);
|
||||
}
|
||||
@@ -571,7 +571,7 @@ var StatementModel = BasicModel.extend({
|
||||
* Force the partial reconciliation to display the reconciliate button.
|
||||
*
|
||||
* @param {string} handle
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
partialReconcile: function(handle, data) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -607,14 +607,14 @@ var StatementModel = BasicModel.extend({
|
||||
* Change the value of the editable proposition line or create a new one.
|
||||
*
|
||||
* If the editable line comes from a reconcile model with 2 lines
|
||||
* and their 'amount_type' is "percent"
|
||||
* and their 'amount_type' is "percent"
|
||||
* and their total equals 100% (this doesn't take into account the taxes
|
||||
* who can be included or not)
|
||||
* Then the total is recomputed to have 100%.
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {*} values
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateProposition: function (handle, values) {
|
||||
var self = this;
|
||||
@@ -624,13 +624,14 @@ var StatementModel = BasicModel.extend({
|
||||
prop = this._formatQuickCreate(line);
|
||||
line.reconciliation_proposition.push(prop);
|
||||
}
|
||||
if (!line.reconciliation_proposition.slice(0,-1).every(function(prop) {return prop.to_check == values.to_check})) {
|
||||
if (!line.reconciliation_proposition.slice(0,-1).every(function(prop) {return prop.to_check == values.to_check;})) {
|
||||
new CrashManager().show_warning({data: {
|
||||
exception_type: _t("Incorrect Operation"),
|
||||
message: _t("You cannot mix items with and without the 'To Check' checkbox ticked.")
|
||||
}});
|
||||
// FIXME: model should not be tied to the DOM !
|
||||
$('.create_to_check input').click();
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
_.each(values, function (value, fieldName) {
|
||||
if (fieldName === 'analytic_tag_ids') {
|
||||
@@ -682,7 +683,7 @@ var StatementModel = BasicModel.extend({
|
||||
* Update the number of validated lines
|
||||
*
|
||||
* @param {(string|string[])} handle
|
||||
* @returns {Deferred<Object>} resolved with an object who contains
|
||||
* @returns {Promise<Object>} resolved with an object who contains
|
||||
* 'handles' key
|
||||
*/
|
||||
validate: function (handle) {
|
||||
@@ -699,9 +700,11 @@ var StatementModel = BasicModel.extend({
|
||||
}
|
||||
var ids = [];
|
||||
var values = [];
|
||||
var handlesPromises = [];
|
||||
_.each(handles, function (handle) {
|
||||
var line = self.getLine(handle);
|
||||
var props = _.filter(line.reconciliation_proposition, function (prop) {return !prop.invalid;});
|
||||
var computeLinePromise;
|
||||
if (props.length === 0) {
|
||||
// Usability: if user has not choosen any lines and click validate, it has the same behavior
|
||||
// as creating a write-off of the same amount.
|
||||
@@ -710,61 +713,65 @@ var StatementModel = BasicModel.extend({
|
||||
}));
|
||||
// update balance of line otherwise it won't be to zero and another line will be added
|
||||
line.reconciliation_proposition.push(props[0]);
|
||||
self._computeLine(line);
|
||||
computeLinePromise = self._computeLine(line);
|
||||
}
|
||||
ids.push(line.id);
|
||||
var values_dict = {
|
||||
"partner_id": line.st_line.partner_id,
|
||||
"counterpart_aml_dicts": _.map(_.filter(props, function (prop) {
|
||||
return !isNaN(prop.id) && !prop.already_paid;
|
||||
}), self._formatToProcessReconciliation.bind(self, line)),
|
||||
"payment_aml_ids": _.pluck(_.filter(props, function (prop) {
|
||||
return !isNaN(prop.id) && prop.already_paid;
|
||||
}), 'id'),
|
||||
"new_aml_dicts": _.map(_.filter(props, function (prop) {
|
||||
return isNaN(prop.id) && prop.display;
|
||||
}), self._formatToProcessReconciliation.bind(self, line)),
|
||||
};
|
||||
line.reconciliation_proposition.some(function(prop) {
|
||||
if (prop.to_check) {
|
||||
values_dict['to_check'] = true;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
if (line.reconciliation_proposition[0].to_check) {
|
||||
values_dict['to_check'] = true;
|
||||
}
|
||||
|
||||
// If the lines are not fully balanced, create an unreconciled amount.
|
||||
// line.st_line.currency_id is never false here because its equivalent to
|
||||
// statement_line.currency_id or statement_line.journal_id.currency_id or statement_line.journal_id.company_id.currency_id (Python-side).
|
||||
// see: get_statement_line_for_reconciliation_widget method in account/models/account_bank_statement.py for more details
|
||||
var currency = session.get_currency(line.st_line.currency_id);
|
||||
var balance = line.balance.amount;
|
||||
if (!utils.float_is_zero(balance, currency.digits[1])) {
|
||||
var unreconciled_amount_dict = {
|
||||
'account_id': line.st_line.open_balance_account_id,
|
||||
'credit': balance > 0 ? balance : 0,
|
||||
'debit': balance < 0 ? -balance : 0,
|
||||
'name': line.st_line.name + ' : ' + _t("Open balance"),
|
||||
handlesPromises.push(Promise.resolve(computeLinePromise).then(function() {
|
||||
var values_dict = {
|
||||
"partner_id": line.st_line.partner_id,
|
||||
"counterpart_aml_dicts": _.map(_.filter(props, function (prop) {
|
||||
return !isNaN(prop.id) && !prop.already_paid;
|
||||
}), self._formatToProcessReconciliation.bind(self, line)),
|
||||
"payment_aml_ids": _.pluck(_.filter(props, function (prop) {
|
||||
return !isNaN(prop.id) && prop.already_paid;
|
||||
}), 'id'),
|
||||
"new_aml_dicts": _.map(_.filter(props, function (prop) {
|
||||
return isNaN(prop.id) && prop.display;
|
||||
}), self._formatToProcessReconciliation.bind(self, line)),
|
||||
};
|
||||
values_dict['new_aml_dicts'].push(unreconciled_amount_dict);
|
||||
}
|
||||
values.push(values_dict);
|
||||
line.reconciled = true;
|
||||
self.valuenow++;
|
||||
line.reconciliation_proposition.some(function(prop) {
|
||||
if (prop.to_check) {
|
||||
values_dict['to_check'] = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (line.reconciliation_proposition[0].to_check) {
|
||||
values_dict['to_check'] = true;
|
||||
}
|
||||
|
||||
// If the lines are not fully balanced, create an unreconciled amount.
|
||||
// line.st_line.currency_id is never false here because its equivalent to
|
||||
// statement_line.currency_id or statement_line.journal_id.currency_id or statement_line.journal_id.company_id.currency_id (Python-side).
|
||||
// see: get_statement_line_for_reconciliation_widget method in account/models/account_bank_statement.py for more details
|
||||
var currency = session.get_currency(line.st_line.currency_id);
|
||||
var balance = line.balance.amount;
|
||||
if (!utils.float_is_zero(balance, currency.digits[1])) {
|
||||
var unreconciled_amount_dict = {
|
||||
'account_id': line.st_line.open_balance_account_id,
|
||||
'credit': balance > 0 ? balance : 0,
|
||||
'debit': balance < 0 ? -balance : 0,
|
||||
'name': line.st_line.name + ' : ' + _t("Open balance"),
|
||||
};
|
||||
values_dict['new_aml_dicts'].push(unreconciled_amount_dict);
|
||||
}
|
||||
values.push(values_dict);
|
||||
line.reconciled = true;
|
||||
self.valuenow++;
|
||||
}));
|
||||
});
|
||||
|
||||
return this._rpc({
|
||||
model: 'account.reconciliation.widget',
|
||||
method: 'process_bank_statement_line',
|
||||
args: [ids, values],
|
||||
context: self.context,
|
||||
})
|
||||
.then(this._validatePostProcess.bind(this))
|
||||
.then(function () {
|
||||
return {handles: handles};
|
||||
});
|
||||
return Promise.all(handlesPromises).then(function() {
|
||||
return self._rpc({
|
||||
model: 'account.reconciliation.widget',
|
||||
method: 'process_bank_statement_line',
|
||||
args: [ids, values],
|
||||
context: self.context,
|
||||
})
|
||||
.then(self._validatePostProcess.bind(self))
|
||||
.then(function () {
|
||||
return {handles: handles};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -779,10 +786,6 @@ var StatementModel = BasicModel.extend({
|
||||
* @param {Object} prop
|
||||
*/
|
||||
_addProposition: function (line, prop) {
|
||||
function checkAccountType (r) {
|
||||
return !isNaN(r.id) && r.account_type !== prop.account_type;
|
||||
}
|
||||
|
||||
line.reconciliation_proposition.push(prop);
|
||||
},
|
||||
/**
|
||||
@@ -793,7 +796,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_blurProposition: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -811,7 +814,7 @@ var StatementModel = BasicModel.extend({
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @param {integer} partner_id
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_changePartner: function (handle, partner_id) {
|
||||
var self = this;
|
||||
@@ -833,7 +836,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {Object} line
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_computeLine: function (line) {
|
||||
//balance_type
|
||||
@@ -886,7 +889,7 @@ var StatementModel = BasicModel.extend({
|
||||
'__focus': false
|
||||
});
|
||||
|
||||
prop.tax_exigible = tax.tax_exigibility === 'on_payment' ? true : undefined
|
||||
prop.tax_exigible = tax.tax_exigibility === 'on_payment' ? true : undefined;
|
||||
prop.amount = tax.base;
|
||||
prop.amount_str = field_utils.format.monetary(Math.abs(prop.amount), {}, formatOptions);
|
||||
prop.invalid = !self._isValid(prop);
|
||||
@@ -904,7 +907,7 @@ var StatementModel = BasicModel.extend({
|
||||
}
|
||||
});
|
||||
|
||||
return $.when.apply($, tax_defs).then(function () {
|
||||
return Promise.all(tax_defs).then(function () {
|
||||
_.each(reconciliation_proposition, function (prop) {
|
||||
prop.__tax_to_recompute = false;
|
||||
});
|
||||
@@ -946,7 +949,7 @@ var StatementModel = BasicModel.extend({
|
||||
});
|
||||
},
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @param {string} handle
|
||||
@@ -1004,7 +1007,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {Object[]} lines
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_formatLine: function (lines) {
|
||||
var self = this;
|
||||
@@ -1028,7 +1031,7 @@ var StatementModel = BasicModel.extend({
|
||||
if(!line.st_line.partner_id && line.reconciliation_proposition.length > 0){
|
||||
var hasDifferentPartners = function(prop){
|
||||
return !prop.partner_id || prop.partner_id != line.reconciliation_proposition[0].partner_id;
|
||||
}
|
||||
};
|
||||
|
||||
if(!_.any(line.reconciliation_proposition, hasDifferentPartners)){
|
||||
return self.changePartner(line.handle, {
|
||||
@@ -1059,7 +1062,7 @@ var StatementModel = BasicModel.extend({
|
||||
})
|
||||
);
|
||||
});
|
||||
return $.when.apply($, defs);
|
||||
return Promise.all(defs);
|
||||
},
|
||||
/**
|
||||
* Format the server value then compute the line
|
||||
@@ -1069,7 +1072,7 @@ var StatementModel = BasicModel.extend({
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @param {Object[]} mv_lines
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_formatMoveLine: function (handle, mv_lines) {
|
||||
var self = this;
|
||||
@@ -1170,7 +1173,7 @@ var StatementModel = BasicModel.extend({
|
||||
if (parseInt(prop['id'])) {
|
||||
excludedIds.push(prop['id']);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
return excludedIds;
|
||||
@@ -1202,7 +1205,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_performMoveLine: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -1263,7 +1266,7 @@ var StatementModel = BasicModel.extend({
|
||||
if (prop.analytic_account_id) result.analytic_account_id = prop.analytic_account_id.id;
|
||||
if (prop.tax_id && !prop.is_tax) result.tax_ids = [[4, prop.tax_id.id, null]];
|
||||
if (prop.tax_id && prop.is_tax) result.tax_line_id = prop.tax_id.id;
|
||||
if (prop.reconcileModelId) result.reconcile_model_id = prop.reconcileModelId
|
||||
if (prop.reconcileModelId) result.reconcile_model_id = prop.reconcileModelId;
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
@@ -1275,7 +1278,7 @@ var StatementModel = BasicModel.extend({
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_validatePostProcess: function (data) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1312,7 +1315,7 @@ var ManualModel = StatementModel.extend({
|
||||
* @param {integer[]} [context.company_ids]
|
||||
* @param {integer[]} [context.partner_ids] used for 'customers' and
|
||||
* 'suppliers' mode
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
load: function (context) {
|
||||
var self = this;
|
||||
@@ -1335,7 +1338,7 @@ var ManualModel = StatementModel.extend({
|
||||
});
|
||||
|
||||
var domainReconcile = [];
|
||||
var company_ids = context && context.company_ids || [session.company_id]
|
||||
var company_ids = context && context.company_ids || [session.company_id];
|
||||
if (company_ids) {
|
||||
domainReconcile.push(['company_id', 'in', company_ids]);
|
||||
}
|
||||
@@ -1350,7 +1353,7 @@ var ManualModel = StatementModel.extend({
|
||||
|
||||
var def_taxes = this._loadTaxes();
|
||||
|
||||
return $.when(def_reconcileModel, def_account, def_taxes).then(function () {
|
||||
return Promise.all([def_reconcileModel, def_account, def_taxes]).then(function () {
|
||||
switch(context.mode) {
|
||||
case 'customers':
|
||||
case 'suppliers':
|
||||
@@ -1399,7 +1402,7 @@ var ManualModel = StatementModel.extend({
|
||||
})
|
||||
.then(function (result) {
|
||||
// Flatten the result
|
||||
self.manualLines = [].concat(result.accounts, result.customers, result.suppliers)
|
||||
self.manualLines = [].concat(result.accounts, result.customers, result.suppliers);
|
||||
self.valuenow = 0;
|
||||
self.valuemax = Object.keys(self.manualLines).length;
|
||||
var lines = self.manualLines.splice(0, self.defaultDisplayQty);
|
||||
@@ -1413,7 +1416,7 @@ var ManualModel = StatementModel.extend({
|
||||
* Load more partners/accounts
|
||||
*
|
||||
* @param {integer} qty quantity to load
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loadMore: function(qty) {
|
||||
if (qty === undefined) {
|
||||
@@ -1427,22 +1430,22 @@ var ManualModel = StatementModel.extend({
|
||||
* Method to load informations on lines
|
||||
*
|
||||
* @param {Array} lines manualLines to load
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loadData: function(lines) {
|
||||
var self = this;
|
||||
var defs = [];
|
||||
_.each(lines, function (l) {
|
||||
defs.push(self._formatLine(l.mode, l))
|
||||
defs.push(self._formatLine(l.mode, l));
|
||||
});
|
||||
return $.when.apply($, defs);
|
||||
return Promise.all(defs);
|
||||
|
||||
},
|
||||
/**
|
||||
* Mark the account or the partner as reconciled
|
||||
*
|
||||
* @param {(string|string[])} handle
|
||||
* @returns {Deferred<Array>} resolved with the handle array
|
||||
* @returns {Promise<Array>} resolved with the handle array
|
||||
*/
|
||||
validate: function (handle) {
|
||||
var self = this;
|
||||
@@ -1457,7 +1460,7 @@ var ManualModel = StatementModel.extend({
|
||||
});
|
||||
}
|
||||
|
||||
var def = $.when();
|
||||
var def = Promise.resolve();
|
||||
var process_reconciliations = [];
|
||||
var reconciled = [];
|
||||
_.each(handles, function (handle) {
|
||||
@@ -1520,7 +1523,7 @@ var ManualModel = StatementModel.extend({
|
||||
}
|
||||
}));
|
||||
});
|
||||
return $.when.apply($, defs).then(function() {
|
||||
return Promise.all(defs).then(function () {
|
||||
if (partner_ids.length) {
|
||||
self._rpc({
|
||||
model: 'res.partner',
|
||||
@@ -1539,11 +1542,11 @@ var ManualModel = StatementModel.extend({
|
||||
|
||||
/**
|
||||
* override change the balance type to display or not the reconcile button
|
||||
*
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {Object} line
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_computeLine: function (line) {
|
||||
return this._super(line).then(function () {
|
||||
@@ -1568,7 +1571,7 @@ var ManualModel = StatementModel.extend({
|
||||
* @private
|
||||
* @param {'customers' | 'suppliers' | 'accounts'} type
|
||||
* @param {Object} data
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_formatLine: function (type, data) {
|
||||
var line = this.lines[_.uniqueId('rline')] = _.extend(data, {
|
||||
@@ -1591,7 +1594,7 @@ var ManualModel = StatementModel.extend({
|
||||
},
|
||||
/**
|
||||
* override to add journal_id
|
||||
*
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {Object} line
|
||||
@@ -1619,12 +1622,11 @@ var ManualModel = StatementModel.extend({
|
||||
* @returns {Object}
|
||||
*/
|
||||
_formatQuickCreate: function (line, values) {
|
||||
var self = this;
|
||||
// Add journal to created line
|
||||
if (values && values.journal_id === undefined && line && line.createForm && line.createForm.journal_id) {
|
||||
values.journal_id = line.createForm.journal_id;
|
||||
}
|
||||
return this._super(line, values)
|
||||
return this._super(line, values);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -1650,7 +1652,7 @@ var ManualModel = StatementModel.extend({
|
||||
* @override
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_performMoveLine: function (handle) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -1675,7 +1677,7 @@ var ManualModel = StatementModel.extend({
|
||||
})
|
||||
.then(this._formatMoveLine.bind(this, handle));
|
||||
},
|
||||
|
||||
|
||||
_formatToProcessReconciliation: function (line, prop) {
|
||||
var result = this._super(line, prop);
|
||||
result['date'] = prop.date;
|
||||
|
||||
@@ -69,7 +69,7 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, {
|
||||
if (this.model.context && this.model.context.args && this.model.context.args.search) {
|
||||
this.$('.reconciliation_search_input').val(self.model.context.args.search);
|
||||
}
|
||||
return $.when.apply($, defs);
|
||||
return Promise.all(defs);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -322,7 +322,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
'toggle': 'popover'
|
||||
});
|
||||
var def2 = this._super.apply(this, arguments);
|
||||
return $.when(def1, def2);
|
||||
return Promise.all([def1, def2]);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -419,41 +419,44 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
|
||||
// create form
|
||||
if (state.createForm) {
|
||||
var createPromise;
|
||||
if (!this.fields.account_id) {
|
||||
this._renderCreate(state);
|
||||
createPromise = this._renderCreate(state);
|
||||
}
|
||||
var data = this.model.get(this.handleCreateRecord).data;
|
||||
this.model.notifyChanges(this.handleCreateRecord, state.createForm).then(function () {
|
||||
// FIXME can't it directly written REPLACE_WITH ids=state.createForm.analytic_tag_ids
|
||||
self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'REPLACE_WITH', ids: []}}).then(function (){
|
||||
var defs = [];
|
||||
_.each(state.createForm.analytic_tag_ids, function (tag) {
|
||||
defs.push(self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'ADD_M2M', ids: tag}}));
|
||||
});
|
||||
$.when.apply($, defs).then(function () {
|
||||
var record = self.model.get(self.handleCreateRecord);
|
||||
_.each(self.fields, function (field, fieldName) {
|
||||
if (self._avoidFieldUpdate[fieldName]) return;
|
||||
if (fieldName === "partner_id") return;
|
||||
if ((data[fieldName] || state.createForm[fieldName]) && !_.isEqual(state.createForm[fieldName], data[fieldName])) {
|
||||
field.reset(record);
|
||||
}
|
||||
if (fieldName === 'tax_id') {
|
||||
if (!state.createForm[fieldName] || state.createForm[fieldName].amount_type === "group") {
|
||||
$('.create_force_tax_included').addClass('d-none');
|
||||
Promise.resolve(createPromise).then(function(){
|
||||
var data = self.model.get(self.handleCreateRecord).data;
|
||||
self.model.notifyChanges(self.handleCreateRecord, state.createForm).then(function () {
|
||||
// FIXME can't it directly written REPLACE_WITH ids=state.createForm.analytic_tag_ids
|
||||
self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'REPLACE_WITH', ids: []}}).then(function (){
|
||||
var defs = [];
|
||||
_.each(state.createForm.analytic_tag_ids, function (tag) {
|
||||
defs.push(self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'ADD_M2M', ids: tag}}));
|
||||
});
|
||||
Promise.all(defs).then(function () {
|
||||
var record = self.model.get(self.handleCreateRecord);
|
||||
_.each(self.fields, function (field, fieldName) {
|
||||
if (self._avoidFieldUpdate[fieldName]) return;
|
||||
if (fieldName === "partner_id") return;
|
||||
if ((data[fieldName] || state.createForm[fieldName]) && !_.isEqual(state.createForm[fieldName], data[fieldName])) {
|
||||
field.reset(record);
|
||||
}
|
||||
else {
|
||||
$('.create_force_tax_included').removeClass('d-none');
|
||||
if (fieldName === 'tax_id') {
|
||||
if (!state.createForm[fieldName] || state.createForm[fieldName].amount_type === "group") {
|
||||
$('.create_force_tax_included').addClass('d-none');
|
||||
}
|
||||
else {
|
||||
$('.create_force_tax_included').removeClass('d-none');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
if(state.createForm.tax_id){
|
||||
// Set the 'Tax Include' field editable or not depending of the 'price_include' value.
|
||||
self.$('.create_force_tax_included input').attr('disabled', state.createForm.tax_id.price_include);
|
||||
}
|
||||
});
|
||||
if(state.createForm.tax_id){
|
||||
// Set the 'Tax Include' field editable or not depending of the 'price_include' value.
|
||||
this.$('.create_force_tax_included input').attr('disabled', state.createForm.tax_id.price_include);
|
||||
}
|
||||
}
|
||||
this.$('.create .add_line').toggle(!!state.balance.amount_currency);
|
||||
},
|
||||
@@ -511,10 +514,11 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
*
|
||||
* @private
|
||||
* @param {object} state - statement line
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_renderCreate: function (state) {
|
||||
var self = this;
|
||||
this.model.makeRecord('account.bank.statement.line', [{
|
||||
return this.model.makeRecord('account.bank.statement.line', [{
|
||||
relation: 'account.account',
|
||||
type: 'many2one',
|
||||
name: 'account_id',
|
||||
@@ -583,7 +587,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
|
||||
self.fields.amount = new basic_fields.FieldFloat(self,
|
||||
'amount', record, {mode: 'edit'});
|
||||
|
||||
|
||||
self.fields.date = new basic_fields.FieldDate(self,
|
||||
'date', record, {mode: 'edit'});
|
||||
|
||||
@@ -597,13 +601,13 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
self.fields.tax_id.appendTo($create.find('.create_tax_id .o_td_field'));
|
||||
self.fields.analytic_account_id.appendTo($create.find('.create_analytic_account_id .o_td_field'));
|
||||
self.fields.analytic_tag_ids.appendTo($create.find('.create_analytic_tag_ids .o_td_field'));
|
||||
self.fields.force_tax_included.appendTo($create.find('.create_force_tax_included .o_td_field'))
|
||||
self.fields.force_tax_included.appendTo($create.find('.create_force_tax_included .o_td_field'));
|
||||
self.fields.label.appendTo($create.find('.create_label .o_td_field'))
|
||||
.then(addRequiredStyle.bind(self, self.fields.label));
|
||||
self.fields.amount.appendTo($create.find('.create_amount .o_td_field'))
|
||||
.then(addRequiredStyle.bind(self, self.fields.amount));
|
||||
self.fields.date.appendTo($create.find('.create_date .o_td_field'))
|
||||
self.fields.to_check.appendTo($create.find('.create_to_check .o_td_field'))
|
||||
self.fields.date.appendTo($create.find('.create_date .o_td_field'));
|
||||
self.fields.to_check.appendTo($create.find('.create_to_check .o_td_field'));
|
||||
self.$('.create').append($create);
|
||||
|
||||
function addRequiredStyle(widget) {
|
||||
@@ -628,7 +632,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
||||
res_model: 'account.reconcile.model',
|
||||
views: [[false, 'form']],
|
||||
target: 'current'
|
||||
},
|
||||
},
|
||||
{
|
||||
on_reverse_breadcrumb: function() {self.trigger_up('reload');},
|
||||
});
|
||||
@@ -836,11 +840,11 @@ var ManualLineRenderer = LineRenderer.extend({
|
||||
* @override
|
||||
* @param {string} handle
|
||||
* @param {number} proposition id (move line id)
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
removeProposition: function (handle, id) {
|
||||
if (!id) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(handle, id);
|
||||
},
|
||||
@@ -879,7 +883,7 @@ var ManualLineRenderer = LineRenderer.extend({
|
||||
defs.push(def);
|
||||
}
|
||||
|
||||
return $.when.apply($, defs).then(function () {
|
||||
return Promise.all(defs).then(function () {
|
||||
if (!self.fields.title_account_id) {
|
||||
return self.fields.partner_id.prependTo(self.$('.accounting_view thead td:eq(1) span:first'));
|
||||
} else {
|
||||
@@ -909,10 +913,11 @@ var ManualLineRenderer = LineRenderer.extend({
|
||||
* @override
|
||||
*/
|
||||
_renderCreate: function (state) {
|
||||
this._super(state);
|
||||
var parentPromise = this._super(state);
|
||||
this.$('.create .create_journal_id').show();
|
||||
this.$('.create .create_date').removeClass('d-none')
|
||||
this.$('.create .create_date').removeClass('d-none');
|
||||
this.$('.create .create_journal_id .o_input').addClass('o_required_modifier');
|
||||
return parentPromise;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -25,10 +25,10 @@ QUnit.module('account', {
|
||||
}, function () {
|
||||
QUnit.module('Reconciliation');
|
||||
|
||||
QUnit.test('Reconciliation form field', function (assert) {
|
||||
QUnit.test('Reconciliation form field', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'account.invoice',
|
||||
data: this.data,
|
||||
@@ -40,11 +40,11 @@ QUnit.module('account', {
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'remove_move_reconcile') {
|
||||
assert.deepEqual(args.args, [22,{"invoice_id":1}], "should call remove_move_reconcile {warning: required focus}");
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'assign_outstanding_credit') {
|
||||
assert.deepEqual(args.args, [4, 20], "should call assign_outstanding_credit {warning: required focus}");
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,9 +46,9 @@ QUnit.module('section_and_note', {
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test('correct display of section and note fields', function (assert) {
|
||||
QUnit.test('correct display of section and note fields', async function (assert) {
|
||||
assert.expect(4);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'invoice',
|
||||
data: this.data,
|
||||
@@ -76,18 +76,18 @@ QUnit.module('section_and_note', {
|
||||
"should have a section class");
|
||||
|
||||
// enter edit mode
|
||||
testUtils.form.clickEdit(form);
|
||||
await testUtils.form.clickEdit(form);
|
||||
|
||||
// editing line should be textarea
|
||||
$tr0 = form.$('tr.o_data_row:eq(0)');
|
||||
testUtils.dom.click($tr0.find('td.o_data_cell'));
|
||||
assert.strictEqual($tr0.find('td.o_data_cell textarea[name="name"]').length, 1,
|
||||
await testUtils.dom.click($tr0.find('td.o_data_cell'));
|
||||
assert.containsOnce($tr0, 'td.o_data_cell textarea[name="name"]',
|
||||
"editing line should be textarea");
|
||||
|
||||
// editing section should be input
|
||||
$tr1 = form.$('tr.o_data_row:eq(1)');
|
||||
testUtils.dom.click($tr1.find('td.o_data_cell'));
|
||||
assert.strictEqual($tr1.find('td.o_data_cell input[name="name"]').length, 1,
|
||||
await testUtils.dom.click($tr1.find('td.o_data_cell'));
|
||||
assert.containsOnce($tr1, 'td.o_data_cell input[name="name"]',
|
||||
"editing section should be input");
|
||||
|
||||
form.destroy();
|
||||
|
||||
@@ -18,16 +18,16 @@ ChangePassword.include({
|
||||
}).then(function (p) {
|
||||
_this._meter = new Meter(_this, new policy.Policy(p), policy.recommendations);
|
||||
});
|
||||
return $.when(
|
||||
return Promise.all([
|
||||
this._super.apply(this, arguments),
|
||||
getPolicy
|
||||
);
|
||||
]);
|
||||
},
|
||||
start: function () {
|
||||
return $.when(
|
||||
return Promise.all([
|
||||
this._meter.insertAfter(this.$('input[name=new_password]')),
|
||||
this._super()
|
||||
);
|
||||
]);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -28,10 +28,10 @@ var PasswordField = fields.InputField.extend({
|
||||
}).then(function (p) {
|
||||
_this._meter = new Meter(_this, new policy.Policy(p), policy.recommendations);
|
||||
});
|
||||
return $.when(
|
||||
return Promise.all([
|
||||
this._super.apply(this, arguments),
|
||||
getPolicy
|
||||
);
|
||||
]);
|
||||
},
|
||||
/**
|
||||
* Add a <meter> next to the input (TODO: move to template?)
|
||||
@@ -42,7 +42,7 @@ var PasswordField = fields.InputField.extend({
|
||||
_renderEdit: function () {
|
||||
var _this = this;
|
||||
var meter = this._meter;
|
||||
return $.when(this._super.apply(this, arguments)).then(function () {
|
||||
return Promise.resolve(this._super.apply(this, arguments)).then(function () {
|
||||
return meter._widgetRenderAndInsert(function (t) {
|
||||
// insertAfter doesn't work and appendTo means the meter is
|
||||
// ignored (as this.$el is an input[type=password])
|
||||
|
||||
@@ -289,9 +289,9 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
|
||||
},
|
||||
|
||||
stop: function(){
|
||||
$('body').unbind("keypress", this.__handler);
|
||||
$('body').unbind("keydown", this.__keydown_handler);
|
||||
$('body').unbind('keyup', this.__keyup_handler);
|
||||
$('body').off("keypress", this.__handler);
|
||||
$('body').off("keydown", this.__keydown_handler);
|
||||
$('body').off('keyup', this.__keyup_handler);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ var FieldFloatScannable = basicFields.FieldFloat.extend({
|
||||
// The barcode_events component intercepts keypresses and releases them when it
|
||||
// appears they are not part of a barcode. But since released keypresses don't
|
||||
// trigger native behaviour (like characters input), we must simulate it.
|
||||
'keypress': '_onKeypress',
|
||||
keypress: '_onKeypress',
|
||||
}),
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -24,9 +24,9 @@ var FieldFloatScannable = basicFields.FieldFloat.extend({
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_renderEdit: function() {
|
||||
_renderEdit: function () {
|
||||
var self = this;
|
||||
$.when(this._super()).then(function () {
|
||||
return Promise.resolve(this._super()).then(function () {
|
||||
self.$input.data('enableBarcode', true);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -56,13 +56,13 @@ FormController.include({
|
||||
* @private
|
||||
* @param {string} barcode sent by the scanner (string generate from keypress series)
|
||||
* @param {Object} activeBarcode: options sent by the field who use barcode features
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_barcodeAddX2MQuantity: function (barcode, activeBarcode) {
|
||||
if (this.mode === 'readonly') {
|
||||
this.do_warn(_t('Error: Document not editable'),
|
||||
_t('To modify this document, please first start edition.'));
|
||||
return new $.Deferred().reject();
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
var record = this.model.get(this.handle);
|
||||
@@ -166,7 +166,7 @@ FormController.include({
|
||||
* @param {Object} current record
|
||||
* @param {string} barcode sent by the scanner (string generate from keypress series)
|
||||
* @param {Object} activeBarcode: options sent by the field who use barcode features
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_barcodeSelectedCandidate: function (candidate, record, barcode, activeBarcode, quantity) {
|
||||
var changes = {};
|
||||
@@ -198,7 +198,7 @@ FormController.include({
|
||||
* @param {Object} current record
|
||||
* @param {string} barcode sent by the scanner (string generate from keypress series)
|
||||
* @param {Object} activeBarcode: options sent by the field who use barcode features
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_barcodeWithoutCandidate: function (record, barcode, activeBarcode) {
|
||||
var changes = {};
|
||||
@@ -267,26 +267,25 @@ FormController.include({
|
||||
* @param {string|function} method defined by the commands options
|
||||
* @param {string} barcode sent by the scanner (string generate from keypress series)
|
||||
* @param {Object} activeBarcode: options sent by the field who use barcode features
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_barcodeActiveScanned: function (method, barcode, activeBarcode) {
|
||||
var self = this;
|
||||
var methodDef;
|
||||
var def = new $.Deferred();
|
||||
if (typeof method === 'string') {
|
||||
methodDef = this[method](barcode, activeBarcode);
|
||||
} else {
|
||||
methodDef = method.call(this, barcode, activeBarcode);
|
||||
}
|
||||
methodDef
|
||||
.done(function () {
|
||||
var record = self.model.get(self.handle);
|
||||
var candidate = self._getBarCodeRecord(record, barcode, activeBarcode);
|
||||
activeBarcode.candidate = candidate;
|
||||
})
|
||||
.always(function () {
|
||||
def.resolve();
|
||||
});
|
||||
var def = new Promise(function (resolve, reject) {
|
||||
if (typeof method === 'string') {
|
||||
methodDef = self[method](barcode, activeBarcode);
|
||||
} else {
|
||||
methodDef = method.call(self, barcode, activeBarcode);
|
||||
}
|
||||
methodDef
|
||||
.then(function () {
|
||||
var record = self.model.get(self.handle);
|
||||
var candidate = self._getBarCodeRecord(record, barcode, activeBarcode);
|
||||
activeBarcode.candidate = candidate;
|
||||
})
|
||||
.then(resolve, resolve);
|
||||
});
|
||||
return def;
|
||||
},
|
||||
/**
|
||||
@@ -296,7 +295,7 @@ FormController.include({
|
||||
* @private
|
||||
* @param {string} barcode sent by the scanner (string generate from keypress series)
|
||||
* @param {DOM Object} target
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_barcodeScanned: function (barcode, target) {
|
||||
var self = this;
|
||||
@@ -325,7 +324,7 @@ FormController.include({
|
||||
if (prefixed && !hasCommand) {
|
||||
self.do_warn(_t('Error: Barcode command is undefined'), barcode);
|
||||
}
|
||||
return self.alive($.when.apply($, defs)).then(function () {
|
||||
return self.alive(Promise.all(defs)).then(function () {
|
||||
if (!prefixed) {
|
||||
// remember the barcode scanned for the quantity listener
|
||||
self.current_barcode = barcode;
|
||||
@@ -421,12 +420,12 @@ FormRenderer.include({
|
||||
*/
|
||||
_barcodeButtonHandler: function ($button, node) {
|
||||
var commands = {};
|
||||
commands.barcode = function () {return $.when();};
|
||||
commands.barcode = function () {return Promise.resolve();};
|
||||
commands['O-BTN.' + node.attrs.barcode_trigger] = function () {
|
||||
if (!$button.hasClass('o_invisible_modifier')) {
|
||||
$button.click();
|
||||
}
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
};
|
||||
var name = node.attrs.name;
|
||||
if (node.attrs.string) {
|
||||
|
||||
@@ -22,7 +22,7 @@ var BarcodeParser = Class.extend({
|
||||
return;
|
||||
}
|
||||
var id = this.nomenclature_id[0];
|
||||
rpc.query({
|
||||
return rpc.query({
|
||||
model: 'barcode.nomenclature',
|
||||
method: 'read',
|
||||
args: [[id], ['name','rule_ids','upc_ean_conv']],
|
||||
@@ -80,7 +80,7 @@ var BarcodeParser = Class.extend({
|
||||
var total = sum1 + 3 * sum2;
|
||||
return Number((10 - total % 10) % 10);
|
||||
},
|
||||
|
||||
|
||||
|
||||
// returns true if the ean is a valid EAN barcode number by checking the control digit.
|
||||
// ean must be a string
|
||||
@@ -185,10 +185,10 @@ var BarcodeParser = Class.extend({
|
||||
|
||||
return match;
|
||||
},
|
||||
|
||||
|
||||
// attempts to interpret a barcode (string encoding a barcode Code-128)
|
||||
// it will return an object containing various information about the barcode.
|
||||
// most importantly :
|
||||
// most importantly :
|
||||
// - code : the barcode
|
||||
// - type : the type of the barcode (e.g. alias, unit product, weighted product...)
|
||||
//
|
||||
@@ -198,7 +198,7 @@ var BarcodeParser = Class.extend({
|
||||
parse_barcode: function(barcode){
|
||||
var parsed_result = {
|
||||
encoding: '',
|
||||
type:'error',
|
||||
type:'error',
|
||||
code:barcode,
|
||||
base_code: barcode,
|
||||
value: 0,
|
||||
@@ -213,7 +213,7 @@ var BarcodeParser = Class.extend({
|
||||
var rule = rules[i];
|
||||
var cur_barcode = barcode;
|
||||
|
||||
if ( rule.encoding === 'ean13' &&
|
||||
if ( rule.encoding === 'ean13' &&
|
||||
this.check_encoding(barcode,'upca') &&
|
||||
this.nomenclature.upc_ean_conv in {'upc2ean':'','always':''} ){
|
||||
cur_barcode = '0' + cur_barcode;
|
||||
|
||||
@@ -51,10 +51,10 @@ QUnit.module('Barcodes', {
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('Button with barcode_trigger', function (assert) {
|
||||
QUnit.test('Button with barcode_trigger', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -84,16 +84,16 @@ QUnit.test('Button with barcode_trigger', function (assert) {
|
||||
_.each(['O','-','B','T','N','.','d','o','i','t','Enter'], triggerKeypressEvent);
|
||||
// O-BTN.dothat (should not call execute_action as the button isn't visible)
|
||||
_.each(['O','-','B','T','N','.','d','o','t','h','a','t','Enter'], triggerKeypressEvent);
|
||||
|
||||
await testUtils.nextTick();
|
||||
assert.verifySteps([], "no warning should be displayed");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('edit, save and cancel buttons', function (assert) {
|
||||
QUnit.test('edit, save and cancel buttons', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -109,33 +109,37 @@ QUnit.test('edit, save and cancel buttons', function (assert) {
|
||||
|
||||
// O-CMD.EDIT
|
||||
_.each(["O","-","C","M","D",".","E","D","I","T","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(form, ".o_form_editable",
|
||||
"should have switched to 'edit' mode");
|
||||
// dummy change to check that it actually saves
|
||||
testUtils.fields.editInput(form.$('.o_field_widget'), 'test');
|
||||
await testUtils.fields.editInput(form.$('.o_field_widget'), 'test');
|
||||
// O-CMD.SAVE
|
||||
_.each(["O","-","C","M","D",".","S","A","V","E","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(form, ".o_form_readonly",
|
||||
"should have switched to 'readonly' mode");
|
||||
assert.verifySteps(['save'], 'should have saved');
|
||||
|
||||
// O-CMD.EDIT
|
||||
_.each(["O","-","C","M","D",".","E","D","I","T","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
// dummy change to check that it correctly discards
|
||||
testUtils.fields.editInput(form.$('.o_field_widget'), 'test');
|
||||
await testUtils.fields.editInput(form.$('.o_field_widget'), 'test');
|
||||
// O-CMD.CANCEL
|
||||
_.each(["O","-","C","M","D",".","D","I","S","C","A","R","D","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(form, ".o_form_readonly",
|
||||
"should have switched to 'readonly' mode");
|
||||
assert.verifySteps(['save'], 'should not have saved');
|
||||
assert.verifySteps([], 'should not have saved');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('pager buttons', function (assert) {
|
||||
QUnit.test('pager buttons', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -150,21 +154,25 @@ QUnit.test('pager buttons', function (assert) {
|
||||
assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet');
|
||||
// O-CMD.PAGER-NEXT
|
||||
_.each(["O","-","C","M","D",".","N","E","X","T","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors');
|
||||
// O-CMD.PAGER-PREV
|
||||
_.each(["O","-","C","M","D",".","P","R","E","V","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet');
|
||||
// O-CMD.PAGER-LAST
|
||||
_.each(["O","-","C","M","D",".","P","A","G","E","R","-","L","A","S","T","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors');
|
||||
// O-CMD.PAGER-FIRST
|
||||
_.each(["O","-","C","M","D",".","P","A","G","E","R","-","F","I","R","S","T","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('do no update form twice after a command barcode scanned', function (assert) {
|
||||
QUnit.test('do no update form twice after a command barcode scanned', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
var delay = barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms;
|
||||
@@ -176,7 +184,7 @@ QUnit.test('do no update form twice after a command barcode scanned', function (
|
||||
},
|
||||
});
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -201,21 +209,23 @@ QUnit.test('do no update form twice after a command barcode scanned', function (
|
||||
|
||||
// switch to next record
|
||||
_.each(["O","-","C","M","D",".","N","E","X","T","Enter"], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
// a first update is done to reload the data (thus followed by a read), but
|
||||
// update shouldn't be called afterwards
|
||||
assert.verifySteps(['read', 'update', 'read']);
|
||||
assert.verifySteps(['update', 'read']);
|
||||
|
||||
_.each(['5','4','3','9','8','2','6','7','1','2','5','2','Enter'], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
// a real barcode has been scanned -> an update should be requested (with
|
||||
// option reload='false', so it isn't followed by a read)
|
||||
assert.verifySteps(['read', 'update', 'read', 'update']);
|
||||
assert.verifySteps(['update']);
|
||||
|
||||
form.destroy();
|
||||
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay;
|
||||
testUtils.mock.unpatch(FormController);
|
||||
});
|
||||
|
||||
QUnit.test('widget field_float_scannable', function (assert) {
|
||||
QUnit.test('widget field_float_scannable', async function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(11);
|
||||
|
||||
@@ -227,7 +237,7 @@ QUnit.test('widget field_float_scannable', function (assert) {
|
||||
int_field: function () {},
|
||||
};
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -250,7 +260,7 @@ QUnit.test('widget field_float_scannable', function (assert) {
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').text(), '4',
|
||||
"should display the correct value in readonly");
|
||||
|
||||
testUtils.form.clickEdit(form);
|
||||
await testUtils.form.clickEdit(form);
|
||||
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').val(), '4',
|
||||
"should display the correct value in edit");
|
||||
@@ -260,13 +270,15 @@ QUnit.test('widget field_float_scannable', function (assert) {
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').get(0), document.activeElement,
|
||||
"int field should be focused");
|
||||
form.$('.o_field_widget[name=int_field]').trigger({type: 'keypress', which: 50, keyCode: 50}); // 2
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').get(0), document.activeElement,
|
||||
"int field should still be focused");
|
||||
form.$('.o_field_widget[name=int_field]').trigger({type: 'keypress', which: 54, keyCode: 54}); // 6
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').get(0), document.activeElement,
|
||||
"int field should still be focused");
|
||||
|
||||
setTimeout(function () {
|
||||
setTimeout(async function () {
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').val(), '426',
|
||||
"should display the correct value in edit");
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').get(0), document.activeElement,
|
||||
@@ -275,6 +287,7 @@ QUnit.test('widget field_float_scannable', function (assert) {
|
||||
assert.verifySteps([], 'should not have done any onchange RPC');
|
||||
|
||||
form.$('.o_field_widget[name=int_field]').trigger('change'); // should trigger the onchange
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.verifySteps(['onchange'], 'should have done the onchange RPC');
|
||||
|
||||
@@ -284,7 +297,7 @@ QUnit.test('widget field_float_scannable', function (assert) {
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('widget barcode_handler', function (assert) {
|
||||
QUnit.test('widget barcode_handler', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var delay = barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms;
|
||||
@@ -299,7 +312,7 @@ QUnit.test('widget barcode_handler', function (assert) {
|
||||
},
|
||||
};
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -324,6 +337,7 @@ QUnit.test('widget barcode_handler', function (assert) {
|
||||
"initial value should be correct");
|
||||
|
||||
_.each(['5','4','3','9','8','2','6','7','1','2','5','2','Enter'], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_field_widget[name=int_field]').val(), '1',
|
||||
"value should have been incremented");
|
||||
|
||||
@@ -333,7 +347,7 @@ QUnit.test('widget barcode_handler', function (assert) {
|
||||
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay;
|
||||
});
|
||||
|
||||
QUnit.test('specification of widget barcode_handler', function (assert) {
|
||||
QUnit.test('specification of widget barcode_handler', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var delay = barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms;
|
||||
@@ -356,7 +370,7 @@ QUnit.test('specification of widget barcode_handler', function (assert) {
|
||||
});
|
||||
fieldRegistry.add('test_barcode_handler', TestBarcodeHandler);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'order',
|
||||
data: this.data,
|
||||
@@ -392,25 +406,28 @@ QUnit.test('specification of widget barcode_handler', function (assert) {
|
||||
|
||||
// scan twice product 1
|
||||
_.each(['1','2','3','4','5','6','7','8','9','0','Enter'], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_data_row:first .o_data_cell:nth(1)').text(), '1',
|
||||
"quantity of line one should have been incremented");
|
||||
_.each(['1','2','3','4','5','6','7','8','9','0','Enter'], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_data_row:first .o_data_cell:nth(1)').text(), '2',
|
||||
"quantity of line one should have been incremented");
|
||||
|
||||
// scan once product 2
|
||||
_.each(['0','9','8','7','6','5','4','3','2','1','Enter'], triggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_data_row:nth(1) .o_data_cell:nth(1)').text(), '1',
|
||||
"quantity of line one should have been incremented");
|
||||
|
||||
testUtils.form.clickSave(form);
|
||||
await testUtils.form.clickSave(form);
|
||||
|
||||
form.destroy();
|
||||
barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay;
|
||||
delete fieldRegistry.map.test_barcode_handler;
|
||||
});
|
||||
|
||||
QUnit.test('specification of widget barcode_handler with keypress and notifyChange', function (assert) {
|
||||
QUnit.test('specification of widget barcode_handler with keypress and notifyChange', async function (assert) {
|
||||
assert.expect(6);
|
||||
var done = assert.async();
|
||||
|
||||
@@ -440,7 +457,7 @@ QUnit.test('specification of widget barcode_handler with keypress and notifyChan
|
||||
});
|
||||
fieldRegistry.add('test_barcode_handler', TestBarcodeHandler);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'order',
|
||||
data: this.data,
|
||||
@@ -464,20 +481,24 @@ QUnit.test('specification of widget barcode_handler with keypress and notifyChan
|
||||
},
|
||||
});
|
||||
_.each(['1','2','3','4','5','6','7','8','9','0','Enter'], triggerKeypressEvent);
|
||||
// Quantity listener should open a dialog.
|
||||
await testUtils.nextTick();
|
||||
// Quantity listener should open a dialog.
|
||||
triggerKeypressEvent('5');
|
||||
await testUtils.nextTick();
|
||||
|
||||
setTimeout(function () {
|
||||
setTimeout(async function () {
|
||||
var keycode = $.ui.keyCode.ENTER;
|
||||
|
||||
assert.strictEqual($('.modal .modal-body').length, 1, 'should open a modal with a quantity as input');
|
||||
assert.strictEqual($('.modal .modal-body .o_set_qty_input').val(), '5', 'the quantity by default in the modal shoud be 5');
|
||||
|
||||
$('.modal .modal-body .o_set_qty_input').val('7');
|
||||
await testUtils.nextTick();
|
||||
|
||||
$('.modal .modal-body .o_set_qty_input').trigger($.Event('keypress', {which: keycode, keyCode: keycode}));
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(form.$('.o_data_row .o_data_cell:nth(1)').text(), '7',
|
||||
"quantity checked should be 7");
|
||||
"quantity checked should be 7");
|
||||
|
||||
assert.verifySteps(['read', 'read']);
|
||||
|
||||
@@ -487,12 +508,12 @@ QUnit.test('specification of widget barcode_handler with keypress and notifyChan
|
||||
done();
|
||||
});
|
||||
});
|
||||
QUnit.test('barcode_scanned only trigger error for active view', function (assert) {
|
||||
QUnit.test('barcode_scanned only trigger error for active view', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
this.data.order_line.fields._barcode_scanned = {string: 'Barcode scanned', type: 'char'};
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'order',
|
||||
data: this.data,
|
||||
@@ -526,7 +547,7 @@ QUnit.test('barcode_scanned only trigger error for active view', function (asser
|
||||
},
|
||||
});
|
||||
|
||||
testUtils.dom.click(form.$('.o_data_row:first'));
|
||||
await testUtils.dom.click(form.$('.o_data_row:first'));
|
||||
|
||||
// We do not trigger on the body since modal and
|
||||
// form view are both inside it.
|
||||
@@ -540,6 +561,7 @@ QUnit.test('barcode_scanned only trigger error for active view', function (asser
|
||||
return $('.modal').trigger($.Event('keypress', {which: keycode, keyCode: keycode}));
|
||||
}
|
||||
_.each(['O','-','B','T','N','.','c','a','n','c','e','l','Enter'], modalTriggerKeypressEvent);
|
||||
await testUtils.nextTick();
|
||||
assert.verifySteps(['warning'], "only one event should be triggered");
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
@@ -152,7 +152,7 @@ var DataImport = AbstractAction.extend({
|
||||
}).then(function (result) {
|
||||
self.importTemplates = result;
|
||||
});
|
||||
return $.when(this._super.apply(this, arguments), def);
|
||||
return Promise.all([this._super.apply(this, arguments), def]);
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
@@ -162,9 +162,9 @@ var DataImport = AbstractAction.extend({
|
||||
this.setup_float_format_picker();
|
||||
this.setup_date_format_picker();
|
||||
|
||||
return $.when(
|
||||
return Promise.all([
|
||||
this._super(),
|
||||
self.create_model().done(function (id) {
|
||||
self.create_model().then(function (id) {
|
||||
self.id = id;
|
||||
self.$('input[name=import_id]').val(id);
|
||||
|
||||
@@ -173,8 +173,8 @@ var DataImport = AbstractAction.extend({
|
||||
cp_content: {$buttons: self.$buttons},
|
||||
};
|
||||
self.updateControlPanel(status);
|
||||
})
|
||||
);
|
||||
}),
|
||||
]);
|
||||
},
|
||||
create_model: function() {
|
||||
return this._rpc({
|
||||
@@ -346,7 +346,7 @@ var DataImport = AbstractAction.extend({
|
||||
method: 'parse_preview',
|
||||
args: [this.id, this.import_options()],
|
||||
kwargs: {context: session.user_context},
|
||||
}).done(function (result) {
|
||||
}).then(function (result) {
|
||||
var signal = result.error ? 'preview_failed' : 'preview_succeeded';
|
||||
self[signal](result);
|
||||
});
|
||||
@@ -567,7 +567,9 @@ var DataImport = AbstractAction.extend({
|
||||
method: 'do',
|
||||
args: [this.id, fields, columns, this.import_options()],
|
||||
kwargs : kwargs,
|
||||
}).then(null, function (error, event) {
|
||||
}).then(null, function (reason) {
|
||||
var error = reason.message;
|
||||
var event = reason.event;
|
||||
// In case of unexpected exception, convert
|
||||
// "JSON-RPC error" to an import failure, and
|
||||
// prevent default handling (warning dialog)
|
||||
@@ -588,7 +590,7 @@ var DataImport = AbstractAction.extend({
|
||||
|| error.message;
|
||||
}
|
||||
|
||||
return $.when({'messages': [{
|
||||
return Promise.resolve({'messages': [{
|
||||
type: 'error',
|
||||
record: false,
|
||||
message: msg,
|
||||
@@ -596,12 +598,14 @@ var DataImport = AbstractAction.extend({
|
||||
}) ;
|
||||
},
|
||||
onvalidate: function () {
|
||||
return this.call_import({ dryrun: true, tracking_disable: true })
|
||||
.done(this.proxy('validated'));
|
||||
var prom = this.call_import({ dryrun: true, tracking_disable: true });
|
||||
prom.then(this.proxy('validated'));
|
||||
return prom;
|
||||
},
|
||||
onimport: function () {
|
||||
var self = this;
|
||||
return this.call_import({ dryrun: false }).done(function (results) {
|
||||
var prom = this.call_import({ dryrun: false });
|
||||
prom.then(function (results) {
|
||||
var message = results.messages;
|
||||
if (!_.any(message, function (message) {
|
||||
return message.type === 'error'; })) {
|
||||
@@ -610,6 +614,7 @@ var DataImport = AbstractAction.extend({
|
||||
}
|
||||
self['import_failed'](results);
|
||||
});
|
||||
return prom;
|
||||
},
|
||||
onimported: function (event, from, to, results) {
|
||||
this.do_notify(_t("Import completed"), _.str.sprintf(_t("%d records were successfully imported"), results.ids.length));
|
||||
|
||||
@@ -19,10 +19,10 @@ QUnit.module('Base Import Mobile Tests', {
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('import button should be hidden in list on mobile screens', function (assert) {
|
||||
QUnit.test('import button should be hidden in list on mobile screens', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var list = createView({
|
||||
var list = await createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
@@ -34,10 +34,10 @@ QUnit.test('import button should be hidden in list on mobile screens', function
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('import button should be hidden in kanban on mobile screens', function (assert) {
|
||||
QUnit.test('import button should be hidden in kanban on mobile screens', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var kanban = createView({
|
||||
var kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
|
||||
@@ -22,31 +22,31 @@ QUnit.module('Base Import Tests', {
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('add import button in list', function(assert) {
|
||||
QUnit.test('add import button in list', async function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var list = createView({
|
||||
var list = await createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: '<tree><field name="foo"/></tree>',
|
||||
});
|
||||
|
||||
assert.ok(list.$buttons.find('.o_button_import:contains(Import)').is(':visible'),
|
||||
assert.isVisible(list.$buttons.find('.o_button_import:contains(Import)'),
|
||||
"should have a visible Import button");
|
||||
|
||||
testUtils.mock.intercept(list, 'do_action', function() {
|
||||
assert.ok(true, "should have triggered a do_action");
|
||||
});
|
||||
|
||||
testUtils.dom.click(list.$buttons.find('.o_button_import:contains(Import)'));
|
||||
await testUtils.dom.click(list.$buttons.find('.o_button_import:contains(Import)'));
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('list without import button', function(assert) {
|
||||
QUnit.test('list without import button', async function(assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var list = createView({
|
||||
var list = await createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
@@ -56,14 +56,14 @@ QUnit.test('list without import button', function(assert) {
|
||||
}
|
||||
});
|
||||
|
||||
assert.ok(!list.$buttons.find('.o_button_import').length, "should not have an Import button");
|
||||
assert.containsNone(list.$buttons, '.o_button_import', 'should not have an Import button');
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('add import button in kanban', function(assert) {
|
||||
QUnit.test('add import button in kanban', async function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var kanban = createView({
|
||||
var kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
@@ -74,21 +74,21 @@ QUnit.test('add import button in kanban', function(assert) {
|
||||
'</t></templates></kanban>',
|
||||
});
|
||||
|
||||
assert.ok(kanban.$buttons.find('.o_button_import:contains(Import)').is(':visible'),
|
||||
assert.isVisible(kanban.$buttons.find('.o_button_import:contains(Import)'),
|
||||
"should have a visible Import button");
|
||||
|
||||
testUtils.mock.intercept(kanban, 'do_action', function() {
|
||||
assert.ok(true, "should have triggered a do_action");
|
||||
});
|
||||
|
||||
testUtils.dom.click(kanban.$buttons.find('.o_button_import:contains(Import)'));
|
||||
await testUtils.dom.click(kanban.$buttons.find('.o_button_import:contains(Import)'));
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('kanban without import button', function(assert) {
|
||||
QUnit.test('kanban without import button', async function(assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var kanban = createView({
|
||||
var kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
@@ -102,7 +102,7 @@ QUnit.test('kanban without import button', function(assert) {
|
||||
}
|
||||
});
|
||||
|
||||
assert.ok(!kanban.$buttons.find('.o_button_import').length, "should not have an Import button");
|
||||
assert.containsNone(kanban.$buttons, '.o_button_import', "should not have an Import button");
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ var AddToBoardMenu = Widget.extend({
|
||||
* information.
|
||||
*
|
||||
* @private
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_addToBoard: function () {
|
||||
var self = this;
|
||||
|
||||
@@ -55,7 +55,7 @@ var BoardController = FormController.extend({
|
||||
/**
|
||||
* Actually save a dashboard
|
||||
*
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_saveDashboard: function () {
|
||||
var board = this.renderer.getBoard();
|
||||
@@ -233,7 +233,7 @@ var BoardRenderer = FormRenderer.extend({
|
||||
* @param {Object} params.context
|
||||
* @param {any[]} params.domain
|
||||
* @param {string} params.viewType
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_createController: function (params) {
|
||||
var self = this;
|
||||
@@ -244,7 +244,7 @@ var BoardRenderer = FormRenderer.extend({
|
||||
.then(function (action) {
|
||||
if (!action) {
|
||||
// the action does not exist anymore
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
var evalContext = new Context(params.context).eval();
|
||||
if (evalContext.group_by && evalContext.group_by.length === 0) {
|
||||
|
||||
@@ -45,13 +45,13 @@ QUnit.module('Dashboard', {
|
||||
}],
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('dashboard basic rendering', function (assert) {
|
||||
QUnit.test('dashboard basic rendering', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -64,7 +64,7 @@ QUnit.test('dashboard basic rendering', function (assert) {
|
||||
|
||||
form.destroy();
|
||||
|
||||
form = createView({
|
||||
form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -84,10 +84,10 @@ QUnit.test('dashboard basic rendering', function (assert) {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('display the no content helper', function (assert) {
|
||||
QUnit.test('display the no content helper', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -108,10 +108,10 @@ QUnit.test('display the no content helper', function (assert) {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('basic functionality, with one sub action', function (assert) {
|
||||
QUnit.test('basic functionality, with one sub action', async function (assert) {
|
||||
assert.expect(25);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -125,7 +125,7 @@ QUnit.test('basic functionality, with one sub action', function (assert) {
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/web/action/load') {
|
||||
assert.step('load action');
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list']],
|
||||
});
|
||||
@@ -135,7 +135,7 @@ QUnit.test('basic functionality, with one sub action', function (assert) {
|
||||
}
|
||||
if (route === '/web/view/edit_custom') {
|
||||
assert.step('edit custom');
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -158,24 +158,24 @@ QUnit.test('basic functionality, with one sub action', function (assert) {
|
||||
|
||||
assert.ok(form.$('.oe_content').is(':visible'), "content is visible");
|
||||
|
||||
testUtils.dom.click(form.$('.oe_fold'));
|
||||
await testUtils.dom.click(form.$('.oe_fold'));
|
||||
|
||||
assert.notOk(form.$('.oe_content').is(':visible'), "content is no longer visible");
|
||||
|
||||
testUtils.dom.click(form.$('.oe_fold'));
|
||||
await testUtils.dom.click(form.$('.oe_fold'));
|
||||
|
||||
assert.ok(form.$('.oe_content').is(':visible'), "content is visible again");
|
||||
assert.verifySteps(['load action', 'edit custom', 'edit custom']);
|
||||
|
||||
assert.strictEqual($('.modal').length, 0, "should have no modal open");
|
||||
|
||||
testUtils.dom.click(form.$('button.oe_dashboard_link_change_layout'));
|
||||
await testUtils.dom.click(form.$('button.oe_dashboard_link_change_layout'));
|
||||
|
||||
assert.strictEqual($('.modal').length, 1, "should have opened a modal");
|
||||
assert.strictEqual($('.modal li[data-layout="2-1"] i.oe_dashboard_selected_layout').length, 1,
|
||||
"should mark currently selected layout");
|
||||
|
||||
testUtils.dom.click($('.modal .oe_dashboard_layout_selector li[data-layout="1-1"]'));
|
||||
await testUtils.dom.click($('.modal .oe_dashboard_layout_selector li[data-layout="1-1"]'));
|
||||
|
||||
assert.strictEqual($('.modal').length, 0, "should have no modal open");
|
||||
assert.containsOnce(form, 'table.oe_dashboard[data-layout="1-1"]',
|
||||
@@ -183,24 +183,24 @@ QUnit.test('basic functionality, with one sub action', function (assert) {
|
||||
|
||||
|
||||
assert.containsOnce(form, '.oe_action', "should have one displayed action");
|
||||
testUtils.dom.click(form.$('span.oe_close'));
|
||||
await testUtils.dom.click(form.$('span.oe_close'));
|
||||
|
||||
assert.strictEqual($('.modal').length, 1, "should have opened a modal");
|
||||
|
||||
// confirm the close operation
|
||||
testUtils.dom.click($('.modal button.btn-primary'));
|
||||
await testUtils.dom.click($('.modal button.btn-primary'));
|
||||
|
||||
assert.strictEqual($('.modal').length, 0, "should have no modal open");
|
||||
assert.containsNone(form, '.oe_action', "should have no displayed action");
|
||||
|
||||
assert.verifySteps(['load action', 'edit custom', 'edit custom', 'edit custom', 'edit custom']);
|
||||
assert.verifySteps(['edit custom', 'edit custom']);
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('views in the dashboard do not have a control panel', function (assert) {
|
||||
QUnit.test('views in the dashboard do not have a control panel', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -213,7 +213,7 @@ QUnit.test('views in the dashboard do not have a control panel', function (asser
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list'], [5, 'form']],
|
||||
});
|
||||
@@ -232,7 +232,7 @@ QUnit.test('views in the dashboard do not have a control panel', function (asser
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('can render an action without view_mode attribute', function (assert) {
|
||||
QUnit.test('can render an action without view_mode attribute', async function (assert) {
|
||||
// The view_mode attribute is automatically set to the 'action' nodes when
|
||||
// the action is added to the dashboard using the 'Add to dashboard' button
|
||||
// in the searchview. However, other dashboard views can be written by hand
|
||||
@@ -242,7 +242,7 @@ QUnit.test('can render an action without view_mode attribute', function (assert)
|
||||
// first view of the action can be used, by default.
|
||||
assert.expect(3);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -259,10 +259,10 @@ QUnit.test('can render an action without view_mode attribute', function (assert)
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/board/static/src/img/layout_1-1-1.png') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
context: '{"b": 2}',
|
||||
domain: '[["foo", "=", "yop"]]',
|
||||
res_model: 'partner',
|
||||
@@ -287,12 +287,12 @@ QUnit.test('can render an action without view_mode attribute', function (assert)
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('can sort a sub list', function (assert) {
|
||||
QUnit.test('can sort a sub list', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
this.data.partner.fields.foo.sortable = true;
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -305,7 +305,7 @@ QUnit.test('can sort a sub list', function (assert) {
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list']],
|
||||
});
|
||||
@@ -321,17 +321,17 @@ QUnit.test('can sort a sub list', function (assert) {
|
||||
assert.strictEqual($('tr.o_data_row').text(), 'yoplalalaabc',
|
||||
"should have correct initial data");
|
||||
|
||||
testUtils.dom.click(form.$('th.o_column_sortable:contains(Foo)'));
|
||||
await testUtils.dom.click(form.$('th.o_column_sortable:contains(Foo)'));
|
||||
|
||||
assert.strictEqual($('tr.o_data_row').text(), 'abclalalayop',
|
||||
"data should have been sorted");
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('can open a record', function (assert) {
|
||||
QUnit.test('can open a record', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -344,7 +344,7 @@ QUnit.test('can open a record', function (assert) {
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list']],
|
||||
});
|
||||
@@ -367,14 +367,14 @@ QUnit.test('can open a record', function (assert) {
|
||||
},
|
||||
});
|
||||
|
||||
testUtils.dom.click(form.$('tr.o_data_row td:contains(yop)'));
|
||||
await testUtils.dom.click(form.$('tr.o_data_row td:contains(yop)'));
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('can open record using action form view', function (assert) {
|
||||
QUnit.test('can open record using action form view', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -387,7 +387,7 @@ QUnit.test('can open record using action form view', function (assert) {
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list'], [5, 'form']],
|
||||
});
|
||||
@@ -412,14 +412,14 @@ QUnit.test('can open record using action form view', function (assert) {
|
||||
},
|
||||
});
|
||||
|
||||
testUtils.dom.click(form.$('tr.o_data_row td:contains(yop)'));
|
||||
await testUtils.dom.click(form.$('tr.o_data_row td:contains(yop)'));
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('can drag and drop a view', function (assert) {
|
||||
assert.expect(4);
|
||||
QUnit.test('can drag and drop a view', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -432,14 +432,14 @@ QUnit.test('can drag and drop a view', function (assert) {
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list']],
|
||||
});
|
||||
}
|
||||
if (route === '/web/view/edit_custom') {
|
||||
assert.step('edit custom');
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -452,20 +452,21 @@ QUnit.test('can drag and drop a view', function (assert) {
|
||||
assert.containsOnce(form, 'td.index_0 .oe_action',
|
||||
"initial action is in column 0");
|
||||
|
||||
testUtils.dom.dragAndDrop(form.$('.oe_dashboard_column.index_0 .oe_header'),
|
||||
await testUtils.dom.dragAndDrop(form.$('.oe_dashboard_column.index_0 .oe_header'),
|
||||
form.$('.oe_dashboard_column.index_1'));
|
||||
assert.containsNone(form, 'td.index_0 .oe_action',
|
||||
"initial action is not in column 0");
|
||||
assert.containsOnce(form, 'td.index_1 .oe_action',
|
||||
"initial action is in in column 1");
|
||||
assert.verifySteps(['edit custom']);
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('twice the same action in a dashboard', function (assert) {
|
||||
QUnit.test('twice the same action in a dashboard', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -479,14 +480,14 @@ QUnit.test('twice the same action in a dashboard', function (assert) {
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list'],[5, 'kanban']],
|
||||
});
|
||||
}
|
||||
if (route === '/web/view/edit_custom') {
|
||||
assert.step('edit custom');
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -510,10 +511,10 @@ QUnit.test('twice the same action in a dashboard', function (assert) {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('non-existing action in a dashboard', function (assert) {
|
||||
QUnit.test('non-existing action in a dashboard', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -531,11 +532,11 @@ QUnit.test('non-existing action in a dashboard', function (assert) {
|
||||
},
|
||||
mockRPC: function (route) {
|
||||
if (route === '/board/static/src/img/layout_1-1-1.png') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === '/web/action/load') {
|
||||
// server answer if the action doesn't exist anymore
|
||||
return $.when(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -547,10 +548,10 @@ QUnit.test('non-existing action in a dashboard', function (assert) {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('clicking on a kanban\'s button should trigger the action', function (assert) {
|
||||
QUnit.test('clicking on a kanban\'s button should trigger the action', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -582,24 +583,24 @@ QUnit.test('clicking on a kanban\'s button should trigger the action', function
|
||||
|
||||
mockRPC: function (route) {
|
||||
if (route === '/board/static/src/img/layout_1-1-1.png') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({res_model: 'partner', view_mode: 'kanban', views: [[false, 'kanban']]});
|
||||
return Promise.resolve({res_model: 'partner', view_mode: 'kanban', views: [[false, 'kanban']]});
|
||||
}
|
||||
if (route === '/web/dataset/search_read') {
|
||||
return $.when({records: [{foo: 'aqualung'}]});
|
||||
return Promise.resolve({records: [{foo: 'aqualung'}]});
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
testUtils.dom.click(form.$('.o_kanban_test').find('button:first'));
|
||||
await testUtils.dom.click(form.$('.o_kanban_test').find('button:first'));
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('subviews are aware of attach in or detach from the DOM', function (assert) {
|
||||
QUnit.test('subviews are aware of attach in or detach from the DOM', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// patch list renderer `on_attach_callback` for the test only
|
||||
@@ -609,7 +610,7 @@ QUnit.test('subviews are aware of attach in or detach from the DOM', function (a
|
||||
}
|
||||
});
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -622,7 +623,7 @@ QUnit.test('subviews are aware of attach in or detach from the DOM', function (a
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list']],
|
||||
});
|
||||
@@ -643,7 +644,7 @@ QUnit.test('subviews are aware of attach in or detach from the DOM', function (a
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('dashboard intercepts custom events triggered by sub controllers', function (assert) {
|
||||
QUnit.test('dashboard intercepts custom events triggered by sub controllers', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
// we patch the ListController to force it to trigger the custom events that
|
||||
@@ -654,7 +655,7 @@ QUnit.test('dashboard intercepts custom events triggered by sub controllers', fu
|
||||
},
|
||||
});
|
||||
|
||||
var board = createView({
|
||||
var board = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -667,7 +668,7 @@ QUnit.test('dashboard intercepts custom events triggered by sub controllers', fu
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({res_model: 'partner', views: [[false, 'list']]});
|
||||
return Promise.resolve({res_model: 'partner', views: [[false, 'list']]});
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -685,10 +686,10 @@ QUnit.test('dashboard intercepts custom events triggered by sub controllers', fu
|
||||
board.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('save actions to dashboard', function (assert) {
|
||||
QUnit.test('save actions to dashboard', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var actionManager = createActionManager({
|
||||
var actionManager = await createActionManager({
|
||||
data: this.data,
|
||||
archs: {
|
||||
'partner,false,list': '<list><field name="foo"/></list>',
|
||||
@@ -700,13 +701,13 @@ QUnit.test('save actions to dashboard', function (assert) {
|
||||
"should save the correct action");
|
||||
assert.strictEqual(args.view_mode, 'list',
|
||||
"should save the correct view type");
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
actionManager.doAction({
|
||||
await actionManager.doAction({
|
||||
id: 1,
|
||||
res_model: 'partner',
|
||||
type: 'ir.actions.act_window',
|
||||
@@ -717,19 +718,19 @@ QUnit.test('save actions to dashboard', function (assert) {
|
||||
"should display the list view");
|
||||
|
||||
// add this action to dashboard
|
||||
testUtils.dom.click($('.o_search_options .o_dropdown button:contains(Favorites)'));
|
||||
testUtils.dom.click($('.o_add_to_board.o_menu_header'));
|
||||
await testUtils.dom.click($('.o_search_options .o_dropdown button:contains(Favorites)'));
|
||||
await testUtils.dom.click($('.o_add_to_board.o_menu_header'));
|
||||
testUtils.fields.editInput($('input.o_add_to_board_input'), 'a name');
|
||||
testUtils.dom.click($('.o_add_to_board_confirm_button'));
|
||||
await testUtils.dom.click($('.o_add_to_board_confirm_button'));
|
||||
|
||||
actionManager.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('save two searches to dashboard', function (assert) {
|
||||
QUnit.test('save two searches to dashboard', async function (assert) {
|
||||
// the second search saved should not be influenced by the first
|
||||
assert.expect(2);
|
||||
|
||||
var actionManager = createActionManager({
|
||||
var actionManager = await createActionManager({
|
||||
data: this.data,
|
||||
archs: {
|
||||
'partner,false,list': '<list><field name="foo"/></list>',
|
||||
@@ -747,13 +748,13 @@ QUnit.test('save two searches to dashboard', function (assert) {
|
||||
}
|
||||
|
||||
filter_count += 1;
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
actionManager.doAction({
|
||||
await actionManager.doAction({
|
||||
id: 1,
|
||||
res_model: 'partner',
|
||||
type: 'ir.actions.act_window',
|
||||
@@ -762,33 +763,33 @@ QUnit.test('save two searches to dashboard', function (assert) {
|
||||
|
||||
var filter_count = 0;
|
||||
// Add a first filter
|
||||
testUtils.dom.click(actionManager.$('.o_filters_menu_button'));
|
||||
testUtils.dom.click(actionManager.$('.o_add_custom_filter'));
|
||||
await testUtils.dom.click(actionManager.$('.o_filters_menu_button'));
|
||||
await testUtils.dom.click(actionManager.$('.o_add_custom_filter'));
|
||||
actionManager.$('.o_searchview_extended_prop_value .o_input').val('a');
|
||||
testUtils.dom.click(actionManager.$('.o_apply_filter'));
|
||||
await testUtils.dom.click(actionManager.$('.o_apply_filter'));
|
||||
// Add it to dashboard
|
||||
testUtils.dom.click(actionManager.$('.o_favorites_menu_button'));
|
||||
testUtils.dom.click(actionManager.$('.o_add_to_board'));
|
||||
testUtils.dom.click(actionManager.$('.o_add_to_board_confirm_button'));
|
||||
await testUtils.dom.click(actionManager.$('.o_favorites_menu_button'));
|
||||
await testUtils.dom.click(actionManager.$('.o_add_to_board'));
|
||||
await testUtils.dom.click(actionManager.$('.o_add_to_board_confirm_button'));
|
||||
// Remove it
|
||||
testUtils.dom.click(actionManager.$('.o_facet_remove'));
|
||||
await testUtils.dom.click(actionManager.$('.o_facet_remove'));
|
||||
|
||||
// Add the second filter
|
||||
testUtils.dom.click(actionManager.$('.o_filters_menu_button'));
|
||||
testUtils.dom.click(actionManager.$('.o_add_custom_filter'));
|
||||
await testUtils.dom.click(actionManager.$('.o_filters_menu_button'));
|
||||
await testUtils.dom.click(actionManager.$('.o_add_custom_filter'));
|
||||
actionManager.$('.o_searchview_extended_prop_value .o_input').val('b');
|
||||
testUtils.dom.click(actionManager.$('.o_apply_filter'));
|
||||
await testUtils.dom.click(actionManager.$('.o_apply_filter'));
|
||||
// Add it to dashboard
|
||||
testUtils.dom.click(actionManager.$('.o_favorites_menu_button'));
|
||||
testUtils.dom.click(actionManager.$('.o_add_to_board'));
|
||||
testUtils.dom.click(actionManager.$('.o_add_to_board_confirm_button'));
|
||||
await testUtils.dom.click(actionManager.$('.o_favorites_menu_button'));
|
||||
await testUtils.dom.click(actionManager.$('.o_add_to_board'));
|
||||
await testUtils.dom.click(actionManager.$('.o_add_to_board_confirm_button'));
|
||||
|
||||
actionManager.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("Views should be loaded in the user's language", function (assert) {
|
||||
QUnit.test("Views should be loaded in the user's language", async function (assert) {
|
||||
assert.expect(2);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -810,7 +811,7 @@ QUnit.test("Views should be loaded in the user's language", function (assert) {
|
||||
'The data should be loaded with the correct context');
|
||||
}
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'list']],
|
||||
});
|
||||
@@ -826,9 +827,9 @@ QUnit.test("Views should be loaded in the user's language", function (assert) {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test("Dashboard should use correct groupby", function (assert) {
|
||||
QUnit.test("Dashboard should use correct groupby", async function (assert) {
|
||||
assert.expect(1);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -845,7 +846,7 @@ QUnit.test("Dashboard should use correct groupby", function (assert) {
|
||||
'user defined groupby should have precedence on action groupby');
|
||||
}
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
context: {
|
||||
group_by: 'some_field',
|
||||
@@ -864,10 +865,10 @@ QUnit.test("Dashboard should use correct groupby", function (assert) {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('click on a cell of pivot view inside dashboard', function (assert) {
|
||||
QUnit.test('click on a cell of pivot view inside dashboard', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: BoardView,
|
||||
model: 'board',
|
||||
data: this.data,
|
||||
@@ -880,7 +881,7 @@ QUnit.test('click on a cell of pivot view inside dashboard', function (assert) {
|
||||
'</form>',
|
||||
mockRPC: function (route) {
|
||||
if (route === '/web/action/load') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
res_model: 'partner',
|
||||
views: [[4, 'pivot']],
|
||||
});
|
||||
@@ -899,7 +900,7 @@ QUnit.test('click on a cell of pivot view inside dashboard', function (assert) {
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
testUtils.dom.click(form.$('.o_pivot .o_pivot_cell_value'));
|
||||
await testUtils.dom.click(form.$('.o_pivot .o_pivot_cell_value'));
|
||||
|
||||
assert.verifySteps(['do action']);
|
||||
|
||||
|
||||
@@ -187,11 +187,11 @@ var LongpollingBus = Bus.extend(ServicesMixin, {
|
||||
self._pollRpc = false;
|
||||
self._onPoll(result);
|
||||
self._poll();
|
||||
}, function (error, ev) {
|
||||
}).guardedCatch(function (result) {
|
||||
self._pollRpc = false;
|
||||
// no error popup if request is interrupted or fails for any reason
|
||||
ev.preventDefault();
|
||||
if (error && error.message === "XmlHttpRequestError abort") {
|
||||
result.event.preventDefault();
|
||||
if (result.message && result.message.message === "XmlHttpRequestError abort") {
|
||||
self._poll();
|
||||
} else {
|
||||
// random delay to avoid massive longpolling
|
||||
|
||||
@@ -21,10 +21,10 @@ QUnit.module('Bus', {
|
||||
LocalStorageServiceMock = AbstractStorageService.extend({storage: new RamStorage()});
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test('notifications received from the longpolling channel', function (assert) {
|
||||
QUnit.test('notifications received from the longpolling channel', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var pollDeferred = $.Deferred();
|
||||
var pollPromise = testUtils.makeTestPromise();
|
||||
|
||||
var parent = new Widget();
|
||||
testUtils.mock.addMockEnvironment(parent, {
|
||||
@@ -35,49 +35,52 @@ QUnit.module('Bus', {
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
assert.step([route, args.channels.join(',')]);
|
||||
assert.step(route + ' - ' + args.channels.join(','));
|
||||
|
||||
pollDeferred = $.Deferred();
|
||||
pollDeferred.abort = (function () {
|
||||
pollPromise = testUtils.makeTestPromise();
|
||||
pollPromise.abort = (function () {
|
||||
this.reject({message: "XmlHttpRequestError abort"}, $.Event());
|
||||
}).bind(pollDeferred);
|
||||
return pollDeferred;
|
||||
}).bind(pollPromise);
|
||||
return pollPromise;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
var widget = new Widget(parent);
|
||||
widget.appendTo($('#qunit-fixture'));
|
||||
await widget.appendTo($('#qunit-fixture'));
|
||||
|
||||
widget.call('bus_service', 'onNotification', this, function (notifications) {
|
||||
assert.step(['notification', notifications]);
|
||||
assert.step('notification - ' + notifications.toString());
|
||||
});
|
||||
widget.call('bus_service', 'addChannel', 'lambda');
|
||||
|
||||
pollDeferred.resolve([{
|
||||
pollPromise.resolve([{
|
||||
id: 1,
|
||||
channel: 'lambda',
|
||||
message: 'beta',
|
||||
}]);
|
||||
pollDeferred.resolve([{
|
||||
await testUtils.nextTick();
|
||||
|
||||
pollPromise.resolve([{
|
||||
id: 2,
|
||||
channel: 'lambda',
|
||||
message: 'epsilon',
|
||||
}]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.verifySteps([
|
||||
["/longpolling/poll", "lambda"],
|
||||
["notification", [["lambda","beta"]]],
|
||||
["/longpolling/poll", "lambda"],
|
||||
["notification", [["lambda","epsilon"]]],
|
||||
["/longpolling/poll", "lambda"]
|
||||
'/longpolling/poll - lambda',
|
||||
'notification - lambda,beta',
|
||||
'/longpolling/poll - lambda',
|
||||
'notification - lambda,epsilon',
|
||||
'/longpolling/poll - lambda',
|
||||
]);
|
||||
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('provide notification ID of 0 by default', function (assert) {
|
||||
QUnit.test('provide notification ID of 0 by default', async function (assert) {
|
||||
// This test is important in order to ensure that we provide the correct
|
||||
// sentinel value 0 when we are not aware of the last notification ID
|
||||
// that we have received. We cannot provide an ID of -1, otherwise it
|
||||
@@ -96,7 +99,7 @@ QUnit.module('Bus', {
|
||||
},
|
||||
});
|
||||
|
||||
var pollDeferred = $.Deferred();
|
||||
var pollPromise = testUtils.makeTestPromise();
|
||||
var parent = new Widget();
|
||||
testUtils.mock.addMockEnvironment(parent, {
|
||||
data: {},
|
||||
@@ -110,18 +113,18 @@ QUnit.module('Bus', {
|
||||
assert.strictEqual(args.last, 0,
|
||||
"provided last notification ID should be 0");
|
||||
|
||||
pollDeferred = $.Deferred();
|
||||
pollDeferred.abort = (function () {
|
||||
pollPromise = testUtils.makeTestPromise();
|
||||
pollPromise.abort = (function () {
|
||||
this.reject({message: "XmlHttpRequestError abort"}, $.Event());
|
||||
}).bind(pollDeferred);
|
||||
return pollDeferred;
|
||||
}).bind(pollPromise);
|
||||
return pollPromise;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
var widget = new Widget(parent);
|
||||
widget.appendTo($('#qunit-fixture'));
|
||||
await widget.appendTo($('#qunit-fixture'));
|
||||
|
||||
// trigger longpolling poll RPC
|
||||
widget.call('bus_service', 'addChannel', 'lambda');
|
||||
@@ -131,13 +134,12 @@ QUnit.module('Bus', {
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('cross tab bus share message from a channel', function (assert) {
|
||||
var done = assert.async();
|
||||
QUnit.test('cross tab bus share message from a channel', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
// master
|
||||
|
||||
var pollDeferredMaster = $.Deferred();
|
||||
var pollPromiseMaster = testUtils.makeTestPromise();
|
||||
|
||||
var parentMaster = new Widget();
|
||||
testUtils.mock.addMockEnvironment(parentMaster, {
|
||||
@@ -148,79 +150,74 @@ QUnit.module('Bus', {
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
assert.step(['master', route, args.channels.join(',')]);
|
||||
assert.step('master' + ' - ' + route + ' - ' + args.channels.join(','));
|
||||
|
||||
pollDeferredMaster = $.Deferred();
|
||||
pollDeferredMaster.abort = (function () {
|
||||
pollPromiseMaster = testUtils.makeTestPromise();
|
||||
pollPromiseMaster.abort = (function () {
|
||||
this.reject({message: "XmlHttpRequestError abort"}, $.Event());
|
||||
}).bind(pollDeferredMaster);
|
||||
return pollDeferredMaster;
|
||||
}).bind(pollPromiseMaster);
|
||||
return pollPromiseMaster;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
var master = new Widget(parentMaster);
|
||||
master.appendTo($('#qunit-fixture'));
|
||||
await master.appendTo($('#qunit-fixture'));
|
||||
|
||||
master.call('bus_service', 'onNotification', master, function (notifications) {
|
||||
assert.step(['master', 'notification', notifications]);
|
||||
assert.step('master - notification - ' + notifications.toString());
|
||||
});
|
||||
master.call('bus_service', 'addChannel', 'lambda');
|
||||
|
||||
// slave
|
||||
|
||||
setTimeout(function () {
|
||||
var parentSlave = new Widget();
|
||||
testUtils.mock.addMockEnvironment(parentSlave, {
|
||||
data: {},
|
||||
services: {
|
||||
bus_service: BusService,
|
||||
local_storage: LocalStorageServiceMock,
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
throw new Error("Can not use the longpolling of the slave client");
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
await testUtils.nextTick();
|
||||
var parentSlave = new Widget();
|
||||
testUtils.mock.addMockEnvironment(parentSlave, {
|
||||
data: {},
|
||||
services: {
|
||||
bus_service: BusService,
|
||||
local_storage: LocalStorageServiceMock,
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
throw new Error("Can not use the longpolling of the slave client");
|
||||
}
|
||||
});
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
var slave = new Widget(parentSlave);
|
||||
slave.appendTo($('#qunit-fixture'));
|
||||
var slave = new Widget(parentSlave);
|
||||
await slave.appendTo($('#qunit-fixture'));
|
||||
|
||||
slave.call('bus_service', 'onNotification', slave, function (notifications) {
|
||||
assert.step(['slave', 'notification', notifications]);
|
||||
});
|
||||
slave.call('bus_service', 'addChannel', 'lambda');
|
||||
slave.call('bus_service', 'onNotification', slave, function (notifications) {
|
||||
assert.step('slave - notification - ' + notifications.toString());
|
||||
});
|
||||
slave.call('bus_service', 'addChannel', 'lambda');
|
||||
|
||||
pollDeferredMaster.resolve([{
|
||||
id: 1,
|
||||
channel: 'lambda',
|
||||
message: 'beta',
|
||||
}]);
|
||||
pollPromiseMaster.resolve([{
|
||||
id: 1,
|
||||
channel: 'lambda',
|
||||
message: 'beta',
|
||||
}]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.verifySteps([
|
||||
["master", "/longpolling/poll", "lambda"],
|
||||
["master", "notification", [["lambda", "beta"]]],
|
||||
["slave", "notification", [["lambda", "beta"]]],
|
||||
["master", "/longpolling/poll", "lambda"],
|
||||
]);
|
||||
assert.verifySteps([
|
||||
'master - /longpolling/poll - lambda',
|
||||
'master - notification - lambda,beta',
|
||||
'slave - notification - lambda,beta',
|
||||
'master - /longpolling/poll - lambda',
|
||||
]);
|
||||
|
||||
parentMaster.destroy();
|
||||
parentSlave.destroy();
|
||||
|
||||
done();
|
||||
|
||||
}, 3);
|
||||
parentMaster.destroy();
|
||||
parentSlave.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('cross tab bus elect new master on master unload', function (assert) {
|
||||
var done = assert.async();
|
||||
QUnit.test('cross tab bus elect new master on master unload', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
// master
|
||||
var pollDeferredMaster = $.Deferred();
|
||||
var pollPromiseMaster = testUtils.makeTestPromise();
|
||||
|
||||
var parentMaster = new Widget();
|
||||
testUtils.mock.addMockEnvironment(parentMaster, {
|
||||
@@ -231,89 +228,87 @@ QUnit.module('Bus', {
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
assert.step(['master', route, args.channels.join(',')]);
|
||||
assert.step('master - ' + route + ' - ' + args.channels.join(','));
|
||||
|
||||
pollDeferredMaster = $.Deferred();
|
||||
pollDeferredMaster.abort = (function () {
|
||||
pollPromiseMaster = testUtils.makeTestPromise();
|
||||
pollPromiseMaster.abort = (function () {
|
||||
this.reject({message: "XmlHttpRequestError abort"}, $.Event());
|
||||
}).bind(pollDeferredMaster);
|
||||
return pollDeferredMaster;
|
||||
}).bind(pollPromiseMaster);
|
||||
return pollPromiseMaster;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
var master = new Widget(parentMaster);
|
||||
master.appendTo($('#qunit-fixture'));
|
||||
await master.appendTo($('#qunit-fixture'));
|
||||
|
||||
master.call('bus_service', 'onNotification', master, function (notifications) {
|
||||
assert.step(['master', 'notification', notifications]);
|
||||
assert.step('master - notification - ' + notifications.toString());
|
||||
});
|
||||
master.call('bus_service', 'addChannel', 'lambda');
|
||||
|
||||
// slave
|
||||
setTimeout(function () {
|
||||
var parentSlave = new Widget();
|
||||
var pollDeferredSlave = $.Deferred();
|
||||
testUtils.mock.addMockEnvironment(parentSlave, {
|
||||
data: {},
|
||||
services: {
|
||||
bus_service: BusService,
|
||||
local_storage: LocalStorageServiceMock,
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
assert.step(['slave', route, args.channels.join(',')]);
|
||||
await testUtils.nextTick();
|
||||
var parentSlave = new Widget();
|
||||
var pollPromiseSlave = testUtils.makeTestPromise();
|
||||
testUtils.mock.addMockEnvironment(parentSlave, {
|
||||
data: {},
|
||||
services: {
|
||||
bus_service: BusService,
|
||||
local_storage: LocalStorageServiceMock,
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/longpolling/poll') {
|
||||
assert.step('slave - ' + route + ' - ' + args.channels.join(','));
|
||||
|
||||
pollDeferredSlave = $.Deferred();
|
||||
pollDeferredSlave.abort = (function () {
|
||||
this.reject({message: "XmlHttpRequestError abort"}, $.Event());
|
||||
}).bind(pollDeferredSlave);
|
||||
return pollDeferredSlave;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
pollPromiseSlave = testUtils.makeTestPromise();
|
||||
pollPromiseSlave.abort = (function () {
|
||||
this.reject({message: "XmlHttpRequestError abort"}, $.Event());
|
||||
}).bind(pollPromiseSlave);
|
||||
return pollPromiseSlave;
|
||||
}
|
||||
});
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
var slave = new Widget(parentSlave);
|
||||
slave.appendTo($('#qunit-fixture'));
|
||||
var slave = new Widget(parentSlave);
|
||||
await slave.appendTo($('#qunit-fixture'));
|
||||
|
||||
slave.call('bus_service', 'onNotification', slave, function (notifications) {
|
||||
assert.step(['slave', 'notification', notifications]);
|
||||
});
|
||||
slave.call('bus_service', 'addChannel', 'lambda');
|
||||
slave.call('bus_service', 'onNotification', slave, function (notifications) {
|
||||
assert.step('slave - notification - ' + notifications.toString());
|
||||
});
|
||||
slave.call('bus_service', 'addChannel', 'lambda');
|
||||
|
||||
pollDeferredMaster.resolve([{
|
||||
id: 1,
|
||||
channel: 'lambda',
|
||||
message: 'beta',
|
||||
}]);
|
||||
pollPromiseMaster.resolve([{
|
||||
id: 1,
|
||||
channel: 'lambda',
|
||||
message: 'beta',
|
||||
}]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
// simulate unloading master
|
||||
master.call('bus_service', '_onUnload');
|
||||
// simulate unloading master
|
||||
master.call('bus_service', '_onUnload');
|
||||
|
||||
pollDeferredSlave.resolve([{
|
||||
id: 2,
|
||||
channel: 'lambda',
|
||||
message: 'gamma',
|
||||
}]);
|
||||
pollPromiseSlave.resolve([{
|
||||
id: 2,
|
||||
channel: 'lambda',
|
||||
message: 'gamma',
|
||||
}]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.verifySteps([
|
||||
["master", "/longpolling/poll", "lambda"],
|
||||
["master", "notification", [["lambda", "beta"]]],
|
||||
["slave", "notification", [["lambda", "beta"]]],
|
||||
["master", "/longpolling/poll", "lambda"],
|
||||
["slave", "/longpolling/poll", "lambda"],
|
||||
["slave", "notification", [["lambda", "gamma"]]],
|
||||
["slave", "/longpolling/poll", "lambda"],
|
||||
]);
|
||||
assert.verifySteps([
|
||||
'master - /longpolling/poll - lambda',
|
||||
'master - notification - lambda,beta',
|
||||
'slave - notification - lambda,beta',
|
||||
'master - /longpolling/poll - lambda',
|
||||
'slave - /longpolling/poll - lambda',
|
||||
'slave - notification - lambda,gamma',
|
||||
'slave - /longpolling/poll - lambda',
|
||||
]);
|
||||
|
||||
parentMaster.destroy();
|
||||
parentSlave.destroy();
|
||||
|
||||
done();
|
||||
|
||||
}, 3);
|
||||
parentMaster.destroy();
|
||||
parentSlave.destroy();
|
||||
});
|
||||
|
||||
});});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,10 +40,8 @@ var CalendarNotification = Notification.extend({
|
||||
},
|
||||
|
||||
'click .link2showed': function() {
|
||||
var self = this;
|
||||
this._rpc({route: '/calendar/notify_ack'}).always(function() {
|
||||
self.destroy();
|
||||
});
|
||||
this._rpc({route: '/calendar/notify_ack'})
|
||||
.then(this.destroy.bind(this), this.destroy.bind(this));
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -84,8 +82,10 @@ WebClient.include({
|
||||
},
|
||||
get_next_calendar_notif: function() {
|
||||
session.rpc("/calendar/notify", {}, {shadow: true})
|
||||
.done(this.display_calendar_notif.bind(this))
|
||||
.fail(function(err, ev) {
|
||||
.then(this.display_calendar_notif.bind(this))
|
||||
.guardedCatch(function(reason) { //
|
||||
var err = reason.message;
|
||||
var ev = reason.event;
|
||||
if(err.code === -32098) {
|
||||
// Prevent the CrashManager to display an error
|
||||
// in case of an xhr error not due to a server error
|
||||
@@ -114,7 +114,7 @@ BasicModel.include({
|
||||
* @private
|
||||
* @param {Object} record
|
||||
* @param {string} fieldName
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_fetchSpecialAttendeeStatus: function (record, fieldName) {
|
||||
var context = record.getContext({fieldName: fieldName});
|
||||
|
||||
@@ -33,10 +33,10 @@ QUnit.module('calendar', {
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test("many2manyattendee widget: basic rendering", function (assert) {
|
||||
QUnit.test("many2manyattendee widget: basic rendering", async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'event',
|
||||
data: this.data,
|
||||
@@ -53,7 +53,7 @@ QUnit.module('calendar', {
|
||||
"the partner ids should be passed as argument");
|
||||
assert.strictEqual(args.args[1], 14,
|
||||
"the event id should be passed as argument");
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
[1, "Jesus", "accepted", 0],
|
||||
[2, "Mahomet", "needsAction", 0],
|
||||
]);
|
||||
|
||||
@@ -37,7 +37,7 @@ QUnit.module('ActivityMenu', {
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('activity menu widget:today meetings', function (assert) {
|
||||
QUnit.test('activity menu widget:today meetings', async function (assert) {
|
||||
assert.expect(8);
|
||||
var self = this;
|
||||
var activityMenu = new ActivityMenu();
|
||||
@@ -45,13 +45,13 @@ QUnit.test('activity menu widget:today meetings', function (assert) {
|
||||
services: this.services,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'systray_get_activities') {
|
||||
return $.when(self.data['calendar.event']['records']);
|
||||
return Promise.resolve(self.data['calendar.event']['records']);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
|
||||
activityMenu.appendTo($('#qunit-fixture'));
|
||||
await activityMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
assert.hasClass(activityMenu.$el,'o_mail_systray_item', 'should be the instance of widget');
|
||||
|
||||
@@ -59,13 +59,13 @@ QUnit.test('activity menu widget:today meetings', function (assert) {
|
||||
assert.ok(true, "should have triggered a do_action");
|
||||
assert.strictEqual(event.data.action.res_id, activityMenu.$('.o_meeting_filter').eq(0).data('res_id'), 'should open particular meeting form view');
|
||||
});
|
||||
testUtils.dom.click(activityMenu.$('.dropdown-toggle'));
|
||||
testUtils.dom.click(activityMenu.$('.o_meeting_filter').eq(0));
|
||||
await testUtils.dom.click(activityMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(activityMenu.$('.o_meeting_filter').eq(0));
|
||||
|
||||
testUtils.mock.intercept(activityMenu, 'do_action', function (event) {
|
||||
assert.strictEqual(event.data.action, "calendar.action_calendar_event", 'should open meeting calendar view in day mode');
|
||||
});
|
||||
testUtils.dom.click(activityMenu.$('.o_mail_preview'));
|
||||
await testUtils.dom.click(activityMenu.$('.o_mail_preview'));
|
||||
|
||||
assert.ok(activityMenu.$('.o_meeting_filter'), "should be a meeting");
|
||||
assert.containsN(activityMenu, '.o_meeting_filter', 2, 'there should be 2 meetings');
|
||||
|
||||
@@ -52,7 +52,7 @@ QUnit.test('dashboard basic rendering', function (assert) {
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'retrieve_sales_dashboard') {
|
||||
assert.ok(true, "should call /retrieve_sales_dashboard");
|
||||
return $.when(dashboard_data);
|
||||
return Promise.resolve(dashboard_data);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -79,12 +79,12 @@ QUnit.test('dashboard set a new target', function (assert) {
|
||||
if (args.method === 'retrieve_sales_dashboard') {
|
||||
// should be called twice: for the first rendering, and after the target update
|
||||
assert.ok(true, "should call /retrieve_sales_dashboard");
|
||||
return $.when(dashboard_data);
|
||||
return Promise.resolve(dashboard_data);
|
||||
}
|
||||
if (args.method === 'modify_target_sales_dashboard') {
|
||||
assert.ok(true, "should call /modify_target_sales_dashboard");
|
||||
dashboard_data[args.args[0]].target = args.args[1];
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -117,7 +117,7 @@ QUnit.test('dashboard: click on a button to execute an action', function (assert
|
||||
'</kanban>',
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'retrieve_sales_dashboard') {
|
||||
return $.when(dashboard_data);
|
||||
return Promise.resolve(dashboard_data);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -151,7 +151,7 @@ QUnit.test('dashboard should be displayed even if there is no content', function
|
||||
domain: [['id', '=', 239]], // no record will match this domain
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'retrieve_sales_dashboard') {
|
||||
return $.when(dashboardData);
|
||||
return Promise.resolve(dashboardData);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
|
||||
@@ -34,7 +34,7 @@ odoo.define('crm.leads.tree', function (require) {
|
||||
self.buttons_template = 'LeadMiningRequestListView.buttons';
|
||||
}
|
||||
});
|
||||
return $.when(this._super.apply(this, arguments), ready);
|
||||
return Promise.all([this._super.apply(this, arguments), ready]);
|
||||
},
|
||||
renderButtons: function () {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -57,7 +57,7 @@ odoo.define('crm.leads.tree', function (require) {
|
||||
self.buttons_template = 'LeadMiningRequestKanbanView.buttons';
|
||||
}
|
||||
});
|
||||
return $.when(this._super.apply(this, arguments), ready);
|
||||
return Promise.all([this._super.apply(this, arguments), ready]);
|
||||
},
|
||||
renderButtons: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
@@ -74,7 +74,7 @@ CalendarController.include({
|
||||
local_context: context,
|
||||
},
|
||||
})
|
||||
.done(function(o) {
|
||||
.then(function(o) {
|
||||
if (o.status === "OK") {
|
||||
Dialog.alert(self, _t("All events have been disconnected from your previous account. You can now restart the synchronization"), {
|
||||
title: _t('Event disconnection success'),
|
||||
@@ -89,9 +89,7 @@ CalendarController.include({
|
||||
title: _t('Accounts'),
|
||||
});
|
||||
}
|
||||
}).always(function () {
|
||||
event.data.on_always();
|
||||
});
|
||||
}).then(event.data.on_always, event.data.on_always);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ odoo.define('google_calendar.calendar_tests', function (require) {
|
||||
var CalendarView = require('web.CalendarView');
|
||||
var testUtils = require('web.test_utils');
|
||||
|
||||
var createAsyncView = testUtils.createAsyncView;
|
||||
var createCalendarView = testUtils.createCalendarView;
|
||||
|
||||
var initialDate = new Date("2016-12-12T08:00:00Z");
|
||||
|
||||
@@ -27,11 +27,11 @@ QUnit.module('Google Calendar', {
|
||||
type: {string: "type", type: "integer"},
|
||||
},
|
||||
records: [
|
||||
{id: 5, user_id: 4, partner_id: 4, name: "event 1", start: "2016-12-13 15:55:05", stop: "2016-12-15 18:55:05", allday: false, partner_ids: [], type: 2},
|
||||
{id: 6, user_id: 4, partner_id: 4, name: "event 2", start: "2016-12-18 08:00:00", stop: "2016-12-18 09:00:00", allday: false, partner_ids: [], type: 3}
|
||||
{id: 5, user_id: 4, partner_id: 4, name: "event 1", start: "2016-12-13 15:55:05", stop: "2016-12-15 18:55:05", allday: false, partner_ids: [], type: 2},
|
||||
{id: 6, user_id: 4, partner_id: 4, name: "event 2", start: "2016-12-18 08:00:00", stop: "2016-12-18 09:00:00", allday: false, partner_ids: [], type: 3}
|
||||
],
|
||||
check_access_rights: function () {
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
},
|
||||
user: {
|
||||
@@ -69,11 +69,10 @@ QUnit.module('Google Calendar', {
|
||||
}
|
||||
}, function () {
|
||||
|
||||
QUnit.test('sync google calendar', function (assert) {
|
||||
QUnit.test('sync google calendar', async function (assert) {
|
||||
assert.expect(6);
|
||||
var done = assert.async();
|
||||
|
||||
createAsyncView({
|
||||
var calendar = await createCalendarView({
|
||||
View: CalendarView,
|
||||
model: 'calendar.event',
|
||||
data: this.data,
|
||||
@@ -93,30 +92,27 @@ QUnit.module('Google Calendar', {
|
||||
this.data['calendar.event'].records.push(
|
||||
{id: 7, user_id: 4, partner_id: 4, name: "event from google calendar", start: "2016-12-28 15:55:05", stop: "2016-12-29 18:55:05", allday: false, partner_ids: [], type: 2}
|
||||
);
|
||||
return $.when({status: 'need_refresh'});
|
||||
return Promise.resolve({status: 'need_refresh'});
|
||||
} else if (route === '/web/dataset/call_kw/calendar.event/search_read') {
|
||||
assert.step(route);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
}).then(function (calendar) {
|
||||
assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the month");
|
||||
|
||||
var $sidebar = calendar.$('.o_calendar_sidebar');
|
||||
|
||||
testUtils.dom.click(calendar.$('.o_google_sync_button'));
|
||||
|
||||
assert.verifySteps([
|
||||
'/web/dataset/call_kw/calendar.event/search_read',
|
||||
'/google_calendar/sync_data',
|
||||
'/web/dataset/call_kw/calendar.event/search_read',
|
||||
], 'should do a search_read before and after the call to sync_data');
|
||||
|
||||
assert.containsN(calendar, '.fc-event', 3, "should now display 3 events on the month");
|
||||
|
||||
calendar.destroy();
|
||||
done();
|
||||
});
|
||||
|
||||
assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the month");
|
||||
|
||||
await testUtils.dom.click(calendar.$('.o_google_sync_button'));
|
||||
|
||||
assert.verifySteps([
|
||||
'/web/dataset/call_kw/calendar.event/search_read',
|
||||
'/google_calendar/sync_data',
|
||||
'/web/dataset/call_kw/calendar.event/search_read',
|
||||
], 'should do a search_read before and after the call to sync_data');
|
||||
|
||||
assert.containsN(calendar, '.fc-event', 3, "should now display 3 events on the month");
|
||||
|
||||
calendar.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ Sidebar.include({
|
||||
if (this.options.viewType === "form") {
|
||||
def = this._addGoogleDocItems(this.env.model, this.env.activeIds[0]);
|
||||
}
|
||||
return $.when(def).then(this._super.bind(this));
|
||||
return Promise.resolve(def).then(this._super.bind(this));
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -30,12 +30,12 @@ Sidebar.include({
|
||||
* @private
|
||||
* @param {string} model
|
||||
* @param {integer} resID
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_addGoogleDocItems: function (model, resID) {
|
||||
var self = this;
|
||||
if (!resID) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
var gdoc_item = _.indexOf(_.pluck(self.items.other, 'classname'), 'oe_share_gdoc');
|
||||
if (gdoc_item !== -1) {
|
||||
|
||||
@@ -15,7 +15,7 @@ var createView = testUtils.createView;
|
||||
var _addGoogleDocItemsOriginal = GoogleDriveSideBar.prototype._addGoogleDocItems;
|
||||
|
||||
var _addGoogleDocItemsMocked = function (model, resID) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
GoogleDriveSideBar.prototype._addGoogleDocItems = _addGoogleDocItemsMocked;
|
||||
@@ -67,10 +67,10 @@ QUnit.module('gdrive_integration', {
|
||||
}, function () {
|
||||
QUnit.module('Google Drive Sidebar');
|
||||
|
||||
QUnit.test('rendering of the google drive attachments in Sidebar', function (assert) {
|
||||
QUnit.test('rendering of the google drive attachments in Sidebar', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
@@ -83,10 +83,10 @@ QUnit.module('gdrive_integration', {
|
||||
if (route === '/web/dataset/call_kw/google.drive.config/get_google_drive_config') {
|
||||
assert.deepEqual(args.args, ['partner', 1],
|
||||
'The route to get google drive config should have been called');
|
||||
return $.when([{id: 27, name: 'Cyberdyne Systems'}]);
|
||||
return Promise.resolve([{id: 27, name: 'Cyberdyne Systems'}]);
|
||||
}
|
||||
if (route === '/web/dataset/call_kw/google.drive.config/search_read'){
|
||||
return $.when([{google_drive_resource_id: "T1000",
|
||||
return Promise.resolve([{google_drive_resource_id: "T1000",
|
||||
google_drive_client_id: "cyberdyne.org",
|
||||
id: 1}]);
|
||||
}
|
||||
@@ -94,7 +94,7 @@ QUnit.module('gdrive_integration', {
|
||||
assert.deepEqual(args.args, [27, 1, 'T1000'],
|
||||
'The route to get the Google url should have been called');
|
||||
// We don't return anything useful, otherwise it will open a new tab
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -106,18 +106,17 @@ QUnit.module('gdrive_integration', {
|
||||
'The button to the google action should be present');
|
||||
|
||||
// click on gdrive sidebar item
|
||||
testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
|
||||
testUtils.dom.click($googleAction);
|
||||
await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
|
||||
await testUtils.dom.click($googleAction);
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('click on the google drive attachments after switching records', function (assert) {
|
||||
QUnit.test('click on the google drive attachments after switching records', async function (assert) {
|
||||
assert.expect(3);
|
||||
var self = this;
|
||||
|
||||
var currentID;
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
@@ -134,10 +133,10 @@ QUnit.module('gdrive_integration', {
|
||||
if (route === '/web/dataset/call_kw/google.drive.config/get_google_drive_config') {
|
||||
assert.deepEqual(args.args, ['partner', 1],
|
||||
'The route to get google drive config should have been called');
|
||||
return $.when([{id: 27, name: 'Cyberdyne Systems'}]);
|
||||
return Promise.resolve([{id: 27, name: 'Cyberdyne Systems'}]);
|
||||
}
|
||||
if (route === '/web/dataset/call_kw/google.drive.config/search_read'){
|
||||
return $.when([{google_drive_resource_id: "T1000",
|
||||
return Promise.resolve([{google_drive_resource_id: "T1000",
|
||||
google_drive_client_id: "cyberdyne.org",
|
||||
id: 1}]);
|
||||
}
|
||||
@@ -145,20 +144,20 @@ QUnit.module('gdrive_integration', {
|
||||
assert.deepEqual(args.args, [27, currentID, 'T1000'],
|
||||
'The route to get the Google url should have been called');
|
||||
// We don't return anything useful, otherwise it will open a new tab
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
currentID = 1;
|
||||
testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
|
||||
testUtils.dom.click(form.sidebar.$('.oe_share_gdoc'));
|
||||
await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
|
||||
await testUtils.dom.click(form.sidebar.$('.oe_share_gdoc'));
|
||||
|
||||
testUtils.dom.click(form.pager.$('.o_pager_next'));
|
||||
await testUtils.dom.click(form.pager.$('.o_pager_next'));
|
||||
currentID = 2;
|
||||
testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
|
||||
testUtils.dom.click(form.sidebar.$('.oe_share_gdoc'));
|
||||
await testUtils.dom.click(form.sidebar.$('.o_dropdown_toggler_btn:contains(Action)'));
|
||||
await testUtils.dom.click(form.sidebar.$('.oe_share_gdoc'));
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ var AddToGoogleSpreadsheetMenu = Widget.extend({
|
||||
var ds = new data.DataSet(this, 'google.drive.config');
|
||||
|
||||
ds.call('set_spreadsheet', [modelName, Domain.prototype.arrayToString(domain), groupBys, list_view_id])
|
||||
.done(function (res) {
|
||||
.then(function (res) {
|
||||
if (res.url){
|
||||
window.open(res.url, '_blank');
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ odoo.define('hr.employee_chat', function (require) {
|
||||
dmChat = self.call('mail_service', 'getChannel', dmChatId);
|
||||
dmChat.detach();
|
||||
});
|
||||
$.when(def);
|
||||
Promise.resolve(def);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ var KioskMode = AbstractAction.extend({
|
||||
});
|
||||
// Make a RPC call every day to keep the session alive
|
||||
self._interval = window.setInterval(this._callServer.bind(this), (60*60*1000*24));
|
||||
return $.when(def, this._super.apply(this, arguments));
|
||||
return Promise.all([def, this._super.apply(this, arguments)]);
|
||||
},
|
||||
|
||||
_onBarcodeScanned: function(barcode) {
|
||||
|
||||
@@ -25,7 +25,7 @@ var MyAttendances = AbstractAction.extend({
|
||||
self.employee = res.length && res[0];
|
||||
});
|
||||
|
||||
return $.when(def, this._super.apply(this, arguments));
|
||||
return Promise.all([def, this._super.apply(this, arguments)]);
|
||||
},
|
||||
|
||||
update_attendance: function () {
|
||||
|
||||
@@ -53,7 +53,7 @@ QUnit.module('HR Attendance', {
|
||||
}, function () {
|
||||
QUnit.module('My attendances (client action)');
|
||||
|
||||
QUnit.test('simple rendering', function (assert) {
|
||||
QUnit.test('simple rendering', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var $target = $('#qunit-fixture');
|
||||
@@ -64,7 +64,7 @@ QUnit.module('HR Attendance', {
|
||||
uid: 1,
|
||||
},
|
||||
});
|
||||
clientAction.appendTo($target);
|
||||
await clientAction.appendTo($target);
|
||||
|
||||
assert.strictEqual(clientAction.$('.o_hr_attendance_kiosk_mode h1').text(), 'Employee A',
|
||||
"should have rendered the client action without crashing");
|
||||
@@ -72,7 +72,7 @@ QUnit.module('HR Attendance', {
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Attendance Kiosk Mode Test', function (assert) {
|
||||
QUnit.test('Attendance Kiosk Mode Test', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var $target = $('#qunit-fixture');
|
||||
@@ -89,12 +89,12 @@ QUnit.module('HR Attendance', {
|
||||
if (args.method === 'attendance_scan' && args.model === 'hr.employee') {
|
||||
|
||||
rpcCount++;
|
||||
return $.when(self.data['hr.employee'].records[0]);
|
||||
return Promise.resolve(self.data['hr.employee'].records[0]);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
clientAction.appendTo($target);
|
||||
await clientAction.appendTo($target);
|
||||
core.bus.trigger('barcode_scanned', 1);
|
||||
core.bus.trigger('barcode_scanned', 1);
|
||||
assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.');
|
||||
@@ -105,7 +105,7 @@ QUnit.module('HR Attendance', {
|
||||
clientAction.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Attendance Greeting Message Test', function (assert) {
|
||||
QUnit.test('Attendance Greeting Message Test', async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var $target = $('#qunit-fixture');
|
||||
@@ -113,7 +113,7 @@ QUnit.module('HR Attendance', {
|
||||
var rpcCount = 0;
|
||||
|
||||
var clientActions = [];
|
||||
function createGreetingMessage (target, barcode){
|
||||
async function createGreetingMessage (target, barcode){
|
||||
var action = {
|
||||
attendance: {
|
||||
check_in: "2018-09-20 13:41:13",
|
||||
@@ -138,18 +138,19 @@ QUnit.module('HR Attendance', {
|
||||
as functional flow.
|
||||
*/
|
||||
createGreetingMessage (target, args.args[0]);
|
||||
return $.when({action: action});
|
||||
return Promise.resolve({action: action});
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
clientAction.appendTo(target);
|
||||
await clientAction.appendTo(target);
|
||||
|
||||
clientActions.push(clientAction);
|
||||
}
|
||||
|
||||
// init - mock coming from kiosk
|
||||
createGreetingMessage ($target, 1);
|
||||
await createGreetingMessage ($target, 1);
|
||||
await testUtils.nextMicrotaskTick();
|
||||
assert.strictEqual(clientActions.length, 1, 'Number of clientAction must = 1.');
|
||||
|
||||
core.bus.trigger('barcode_scanned', 1);
|
||||
@@ -161,15 +162,19 @@ QUnit.module('HR Attendance', {
|
||||
assert.strictEqual(rpcCount, 0, 'RPC call should not have been done.');
|
||||
|
||||
core.bus.trigger('barcode_scanned', 2);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(clientActions.length, 2, 'Number of clientActions must = 2.');
|
||||
assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.');
|
||||
core.bus.trigger('barcode_scanned', 2);
|
||||
await testUtils.nextMicrotaskTick();
|
||||
assert.strictEqual(clientActions.length, 2, 'Number of clientActions must = 2.');
|
||||
assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.');
|
||||
|
||||
core.bus.trigger('barcode_scanned', 1);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(clientActions.length, 3, 'Number of clientActions must = 3.');
|
||||
core.bus.trigger('barcode_scanned', 1);
|
||||
await testUtils.nextMicrotaskTick();
|
||||
assert.strictEqual(clientActions.length, 3, 'Number of clientActions must = 3.');
|
||||
assert.strictEqual(rpcCount, 2, 'RPC call should have been done only twice.');
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ var FieldOrgChart = AbstractField.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {integer} employee_id
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_getOrgData: function (employee_id) {
|
||||
var self = this;
|
||||
@@ -52,7 +52,7 @@ var FieldOrgChart = AbstractField.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {integer} employee_id
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_getSubordinatesData: function (employee_id, type) {
|
||||
return this.dm.add(this._rpc({
|
||||
@@ -134,7 +134,7 @@ var FieldOrgChart = AbstractField.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {MouseEvent} event
|
||||
* @returns {Deferred} action loaded
|
||||
* @returns {Promise} action loaded
|
||||
*/
|
||||
_onEmployeeRedirect: function (event) {
|
||||
event.preventDefault();
|
||||
@@ -154,7 +154,7 @@ var FieldOrgChart = AbstractField.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {MouseEvent} event
|
||||
* @returns {Deferred} action loaded
|
||||
* @returns {Promise} action loaded
|
||||
*/
|
||||
_onEmployeeSubRedirect: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -21,10 +21,10 @@ QUnit.module('hr_org_chart', {
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test("hr org chart: empty render", function (assert) {
|
||||
QUnit.test("hr org chart: empty render", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'hr_employee',
|
||||
data: this.data,
|
||||
@@ -36,7 +36,7 @@ QUnit.module('hr_org_chart', {
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/hr/get_org_chart') {
|
||||
assert.ok('employee_id' in args, "it should have 'employee_id' as argument");
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
children: [],
|
||||
managers: [],
|
||||
managers_more: false,
|
||||
@@ -45,13 +45,14 @@ QUnit.module('hr_org_chart', {
|
||||
return this._super(route, args);
|
||||
}
|
||||
});
|
||||
assert.strictEqual(form.$('[name="child_ids"]').children().length, 1, "the chart should have 1 child");
|
||||
assert.strictEqual(form.$('[name="child_ids"]').children().length, 1,
|
||||
"the chart should have 1 child");
|
||||
form.destroy();
|
||||
});
|
||||
QUnit.test("hr org chart: basic render", function (assert) {
|
||||
QUnit.test("hr org chart: basic render", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'hr_employee',
|
||||
data: this.data,
|
||||
@@ -69,7 +70,7 @@ QUnit.module('hr_org_chart', {
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/hr/get_org_chart') {
|
||||
assert.ok('employee_id' in args, "it should have 'employee_id' as argument");
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
children: [{
|
||||
direct_sub_count: 0,
|
||||
indirect_sub_count: 0,
|
||||
@@ -101,10 +102,10 @@ QUnit.module('hr_org_chart', {
|
||||
"the current employee should only be displayed once in the chart");
|
||||
form.destroy();
|
||||
});
|
||||
QUnit.test("hr org chart: basic manager render", function (assert) {
|
||||
QUnit.test("hr org chart: basic manager render", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'hr_employee',
|
||||
data: this.data,
|
||||
@@ -122,7 +123,7 @@ QUnit.module('hr_org_chart', {
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/hr/get_org_chart') {
|
||||
assert.ok('employee_id' in args, "should have 'employee_id' as argument");
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
children: [{
|
||||
direct_sub_count: 0,
|
||||
indirect_sub_count: 0,
|
||||
|
||||
@@ -153,7 +153,7 @@ var ResumeLineRenderer = AbstractGroupedOne2ManyRenderer.extend({
|
||||
|
||||
_render: function () {
|
||||
var self = this;
|
||||
this._super().then(function () {
|
||||
return this._super().then(function () {
|
||||
// Allow to sort records
|
||||
self.$el.find('.o_list_view').sortable({
|
||||
axis: 'y',
|
||||
|
||||
@@ -131,9 +131,9 @@ odoo.define('hr_skills.field_one_to_many_group_tests', function (require) {
|
||||
};
|
||||
}
|
||||
}, function () {
|
||||
QUnit.test('resumé one2many field group by field render', function (assert) {
|
||||
QUnit.test('resumé one2many field group by field render', async function (assert) {
|
||||
assert.expect(16);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
@@ -179,9 +179,9 @@ odoo.define('hr_skills.field_one_to_many_group_tests', function (require) {
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
QUnit.test('resumé one2many field group by field create', function (assert) {
|
||||
QUnit.test('resumé one2many field group by field create', async function (assert) {
|
||||
assert.expect(5);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
@@ -221,22 +221,22 @@ odoo.define('hr_skills.field_one_to_many_group_tests', function (require) {
|
||||
},
|
||||
});
|
||||
|
||||
testUtils.form.clickEdit(form);
|
||||
testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')[0]);
|
||||
await testUtils.form.clickEdit(form);
|
||||
await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')[0]);
|
||||
|
||||
// Fill line form (type should be set from the add button context)
|
||||
testUtils.fields.editInput($('input[name="name"]'), 'new line');
|
||||
testUtils.fields.editInput($('textarea[name="description"]'), 'new description');
|
||||
testUtils.fields.editSelect($('input[name="date_start"]'), '2025-01-01');
|
||||
testUtils.fields.editSelect($('input[name="date_end"]'), '2030-01-01');
|
||||
testUtils.modal.clickButton('Save & Close');
|
||||
await testUtils.fields.editInput($('input[name="name"]'), 'new line');
|
||||
await testUtils.fields.editInput($('textarea[name="description"]'), 'new description');
|
||||
await testUtils.fields.editSelect($('input[name="date_start"]'), '2025-01-01');
|
||||
await testUtils.fields.editSelect($('input[name="date_end"]'), '2030-01-01');
|
||||
await testUtils.modal.clickButton('Save & Close');
|
||||
|
||||
testUtils.form.clickSave(form);
|
||||
await testUtils.form.clickSave(form);
|
||||
form.destroy();
|
||||
});
|
||||
QUnit.test('resumé one2many field group by field delete', function (assert) {
|
||||
QUnit.test('resumé one2many field group by field delete', async function (assert) {
|
||||
assert.expect(2);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
@@ -274,16 +274,16 @@ odoo.define('hr_skills.field_one_to_many_group_tests', function (require) {
|
||||
},
|
||||
});
|
||||
|
||||
testUtils.form.clickEdit(form);
|
||||
testUtils.dom.click(form.$('.o_list_record_remove')[0]);
|
||||
await testUtils.form.clickEdit(form);
|
||||
await testUtils.dom.click(form.$('.o_list_record_remove')[0]);
|
||||
|
||||
testUtils.form.clickSave(form);
|
||||
await testUtils.form.clickSave(form);
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('skills one2many field group by field render', function (assert) {
|
||||
QUnit.test('skills one2many field group by field render', async function (assert) {
|
||||
assert.expect(13);
|
||||
var form = createView({
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
|
||||
@@ -79,7 +79,7 @@ var LivechatButton = Widget.extend({
|
||||
ready = session.rpc('/im_livechat/init', {channel_id: this.options.channel_id})
|
||||
.then(function (result) {
|
||||
if (!result.available_for_me) {
|
||||
return $.Deferred().reject();
|
||||
return Promise.reject();
|
||||
}
|
||||
self._rule = result.rule;
|
||||
});
|
||||
@@ -209,7 +209,7 @@ var LivechatButton = Widget.extend({
|
||||
this._openingChat = true;
|
||||
clearTimeout(this._autoPopupTimeout);
|
||||
if (cookie) {
|
||||
def = $.when(JSON.parse(cookie));
|
||||
def = Promise.resolve(JSON.parse(cookie));
|
||||
} else {
|
||||
this._messages = []; // re-initialize messages cache
|
||||
def = session.rpc('/im_livechat/get_session', {
|
||||
@@ -235,7 +235,9 @@ var LivechatButton = Widget.extend({
|
||||
utils.set_cookie('im_livechat_session', JSON.stringify(self._livechat.toData()), 60*60);
|
||||
utils.set_cookie('im_livechat_auto_popup', JSON.stringify(false), 60*60);
|
||||
}
|
||||
}).always(function () {
|
||||
}).then(function () {
|
||||
self._openingChat = false;
|
||||
}).guardedCatch(function() {
|
||||
self._openingChat = false;
|
||||
});
|
||||
}, 200, true),
|
||||
@@ -270,7 +272,7 @@ var LivechatButton = Widget.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} message
|
||||
* @return {$.Deferred}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_sendMessage: function (message) {
|
||||
var self = this;
|
||||
@@ -337,8 +339,8 @@ var LivechatButton = Widget.extend({
|
||||
ev.stopPropagation();
|
||||
var self = this;
|
||||
var messageData = ev.data.messageData;
|
||||
this._sendMessage(messageData).fail(function (error, e) {
|
||||
e.preventDefault();
|
||||
this._sendMessage(messageData).guardedCatch(function (reason) {
|
||||
reason.event.preventDefault();
|
||||
return self._sendMessage(messageData); // try again just in case
|
||||
});
|
||||
},
|
||||
|
||||
@@ -130,7 +130,7 @@ var WebsiteLivechat = AbstractThread.extend(ThreadTypingMixin, {
|
||||
* @private
|
||||
* @param {Object} params
|
||||
* @param {boolean} params.typing
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_notifyMyselfTyping: function (params) {
|
||||
return session.rpc('/im_livechat/notify_typing', {
|
||||
|
||||
@@ -17,7 +17,7 @@ var Widget = require('web.Widget');
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('Website Livechat Window', {});
|
||||
|
||||
QUnit.test('basic rendering', function (assert) {
|
||||
QUnit.test('basic rendering', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var parent = new Widget();
|
||||
@@ -46,7 +46,7 @@ QUnit.test('basic rendering', function (assert) {
|
||||
|
||||
var chatWindow = new ChatWindow(parent, livechat);
|
||||
testUtils.mock.addMockEnvironment(parent, {});
|
||||
chatWindow.appendTo($('#qunit-fixture'));
|
||||
await chatWindow.appendTo($('#qunit-fixture'));
|
||||
chatWindow.render();
|
||||
|
||||
assert.containsOnce(chatWindow, '.o_thread_window_header',
|
||||
|
||||
@@ -62,7 +62,7 @@ MailManager.include({
|
||||
* Overrides to filter out the Support channel from the previews.
|
||||
*
|
||||
* @override
|
||||
* @returns {$.Promise<Object[]>} list of valid objects for mail.Preview
|
||||
* @returns {Promise<Object[]>} list of valid objects for mail.Preview
|
||||
* template
|
||||
*/
|
||||
getChannelPreviews: function () {
|
||||
@@ -97,7 +97,7 @@ MailManager.include({
|
||||
*
|
||||
* @param {string} [channelState='open'] state of the Support
|
||||
* channel (see CHANNEL_STATES for accepted values)
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
startSupportLivechat: function (channelState) {
|
||||
var self = this;
|
||||
@@ -130,15 +130,15 @@ MailManager.include({
|
||||
self.supportChannelUUID = channel.uuid;
|
||||
|
||||
// add the channel to the MailManager
|
||||
self._addChannel(_.extend(channel, {
|
||||
return self._addChannel(_.extend(channel, {
|
||||
id: channel.uuid,
|
||||
is_minimized: _.contains(['open', 'folded'], channelState),
|
||||
state: channelState,
|
||||
}));
|
||||
|
||||
// display an automatic message in the channel
|
||||
var supportChannel = self.getChannel(channel.uuid);
|
||||
supportChannel.addDefaultMessage();
|
||||
})).then(function () {
|
||||
// display an automatic message in the channel
|
||||
var supportChannel = self.getChannel(channel.uuid);
|
||||
supportChannel.addDefaultMessage();
|
||||
});
|
||||
} else {
|
||||
// the channel has already been added to the MailManager, so
|
||||
// simply re-open it
|
||||
@@ -149,7 +149,7 @@ MailManager.include({
|
||||
channel.fold(channelState === 'folded');
|
||||
}
|
||||
}
|
||||
}).fail(function () {
|
||||
}).guardedCatch(function () {
|
||||
self.do_warn(_t("The Support server can't be reached."));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -249,7 +249,7 @@ var SupportChannel = SearchableThread.extend({
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @return {$.Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_postMessage: function (data) {
|
||||
// ensure that the poll is active before posting the message
|
||||
|
||||
@@ -51,10 +51,10 @@ var SupportMessage = Message.extend({
|
||||
* channel (which are records on the Support database).
|
||||
*
|
||||
* @override
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
toggleStarStatus: function () {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function addMockSupportEnvironment(widget, params) {
|
||||
var originalRPC = supportSession.rpc;
|
||||
var defaultMockSupportRPC = function (route, args) {
|
||||
if (route === '/odoo_im_support/get_support_channel') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
available: true,
|
||||
channel_type: 'livechat',
|
||||
public: 'private',
|
||||
@@ -37,13 +37,13 @@ function addMockSupportEnvironment(widget, params) {
|
||||
});
|
||||
}
|
||||
if (route === '/odoo_im_support/fetch_messages') { // fetching history
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
if (route === '/odoo_im_support/chat_post') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === '/longpolling/support_poll') {
|
||||
return $.Deferred();
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
supportSession.rpc = function (route, args) {
|
||||
|
||||
@@ -32,7 +32,7 @@ QUnit.module('systray', {
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('messaging menu does not display the Support channel if not available', function (assert) {
|
||||
QUnit.test('messaging menu does not display the Support channel if not available', async function (assert) {
|
||||
// the Support channel should only be displayed if a support_token and a support_origin are
|
||||
// specified in the session, which is not the case for this test
|
||||
assert.expect(1);
|
||||
@@ -42,7 +42,7 @@ QUnit.test('messaging menu does not display the Support channel if not available
|
||||
data: this.data,
|
||||
services: this.services,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsNone(messagingMenu, '.o_mail_systray_dropdown_bottom .o_mail_preview[data-preview-id=SupportChannel]', "should not display the Support channel");
|
||||
|
||||
@@ -29,7 +29,7 @@ QUnit.module('systray', {
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('messaging menu displays the Support channel', function (assert) {
|
||||
QUnit.test('messaging menu displays the Support channel', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -38,15 +38,16 @@ QUnit.test('messaging menu displays the Support channel', function (assert) {
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_systray_dropdown_bottom .o_mail_preview[data-preview-id=SupportChannel]', "should display the Support channel");
|
||||
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('clicking on Support channel: channel not available', function (assert) {
|
||||
QUnit.test('clicking on Support channel: channel not available', async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -71,12 +72,13 @@ QUnit.test('clicking on Support channel: channel not available', function (asser
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_systray_dropdown_bottom .o_mail_preview[data-preview-id=SupportChannel]');
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open a chat window");
|
||||
assert.strictEqual($('.o_thread_window .o_thread_window_title').text().trim(), 'Support (offline)',
|
||||
@@ -94,7 +96,7 @@ QUnit.test('clicking on Support channel: channel not available', function (asser
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('clicking on Support channel: channel available', function (assert) {
|
||||
QUnit.test('clicking on Support channel: channel available', async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -115,12 +117,13 @@ QUnit.test('clicking on Support channel: channel available', function (assert) {
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_systray_dropdown_bottom .o_mail_preview[data-preview-id=SupportChannel]');
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open a chat window");
|
||||
assert.strictEqual($('.o_thread_window .o_thread_window_title').text().trim(), 'Support',
|
||||
@@ -138,7 +141,7 @@ QUnit.test('clicking on Support channel: channel available', function (assert) {
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('post messages in Support channel', function (assert) {
|
||||
QUnit.test('post messages in Support channel', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -159,18 +162,18 @@ QUnit.test('post messages in Support channel', function (assert) {
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_systray_dropdown_bottom .o_mail_preview[data-preview-id=SupportChannel]');
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
assert.strictEqual($('.o_thread_window .o_composer_input').length, 1,
|
||||
"should have a composer");
|
||||
|
||||
$('.o_thread_window .o_composer_input .o_input')
|
||||
.val('some message')
|
||||
.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER}));
|
||||
await testUtils.fields.editInput($('.o_thread_window .o_composer_input .o_input'), 'some message');
|
||||
await testUtils.fields.triggerKeydown($('.o_thread_window .o_composer_input .o_input'), 'enter');
|
||||
|
||||
assert.verifySteps([
|
||||
'/mail/init_messaging',
|
||||
@@ -183,7 +186,7 @@ QUnit.test('post messages in Support channel', function (assert) {
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('fold Support channel', function (assert) {
|
||||
QUnit.test('fold Support channel', async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -204,23 +207,24 @@ QUnit.test('fold Support channel', function (assert) {
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
testUtils.mock.intercept(messagingMenu, 'call_service', function (ev) {
|
||||
if (ev.data.service === 'local_storage') {
|
||||
assert.step('LocalStorage: ' + ev.data.method + ' ' + ev.data.args);
|
||||
}
|
||||
}, true);
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_systray_dropdown_bottom .o_mail_preview[data-preview-id=SupportChannel]');
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview[data-preview-id=SupportChannel]'));
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open a chat window");
|
||||
|
||||
// fold, re-open and close channel
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
await testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
await testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_close'));
|
||||
|
||||
assert.verifySteps([
|
||||
@@ -237,7 +241,7 @@ QUnit.test('fold Support channel', function (assert) {
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('restore Support channel if necessary', function (assert) {
|
||||
QUnit.test('restore Support channel if necessary', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -259,21 +263,22 @@ QUnit.test('restore Support channel if necessary', function (assert) {
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open a chat window");
|
||||
|
||||
assert.verifySteps([
|
||||
'/mail/init_messaging',
|
||||
'cors: /odoo_im_support/get_support_channel',
|
||||
'/mail/init_messaging',
|
||||
'cors: /odoo_im_support/fetch_messages',
|
||||
]);
|
||||
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('receive messages in the Support channel', function (assert) {
|
||||
QUnit.test('receive messages in the Support channel', async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var supportChannelID;
|
||||
@@ -297,7 +302,8 @@ QUnit.test('receive messages in the Support channel', function (assert) {
|
||||
services: this.services,
|
||||
session: this.supportParams,
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open a chat window");
|
||||
@@ -312,6 +318,7 @@ QUnit.test('receive messages in the Support channel', function (assert) {
|
||||
};
|
||||
var notification = [[false, 'mail.channel', 1], data];
|
||||
messagingMenu.call('support_bus_service', 'trigger', 'notification', [notification]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.strictEqual($('.o_thread_window .o_thread_message').length, 1,
|
||||
"there should be a new message in the thread");
|
||||
@@ -323,8 +330,8 @@ QUnit.test('receive messages in the Support channel', function (assert) {
|
||||
'A message', "message is correct");
|
||||
|
||||
assert.verifySteps([
|
||||
'/mail/init_messaging',
|
||||
'cors: /odoo_im_support/get_support_channel',
|
||||
'/mail/init_messaging',
|
||||
'cors: /odoo_im_support/fetch_messages',
|
||||
]);
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ var LunchKanbanController = KanbanController.extend({
|
||||
self.$('.o_lunch_kanban').prepend(self.widget.$el);
|
||||
});
|
||||
});
|
||||
return $.when(def, this._super.apply(self, arguments));
|
||||
return Promise.all([def, this._super.apply(self, arguments)]);
|
||||
},
|
||||
/**
|
||||
* Override to add the location domain (coming from the lunchKanbanWidget)
|
||||
|
||||
@@ -76,7 +76,7 @@ var LunchKanbanModel = KanbanModel.extend({
|
||||
},
|
||||
_updateLocation: function (locationId) {
|
||||
this.locationId = locationId;
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
_updateUser: function (userId) {
|
||||
var self = this;
|
||||
|
||||
@@ -2,12 +2,12 @@ odoo.define('lunch.LunchKanbanWidget', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var field_registry = require('web.field_registry');
|
||||
var relationalFields = require('web.relational_fields');
|
||||
var session = require('web.session');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
var FieldMany2One = field_registry.get('many2one');
|
||||
var FieldMany2One = relationalFields.FieldMany2One;
|
||||
|
||||
|
||||
var LunchMany2One = FieldMany2One.extend({
|
||||
|
||||
@@ -23,10 +23,10 @@ QUnit.module('Views', {
|
||||
}, function () {
|
||||
QUnit.module('LunchKanbanView');
|
||||
|
||||
QUnit.test('Simple rendering of LunchKanbanView', function (assert) {
|
||||
QUnit.test('Simple rendering of LunchKanbanView', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
var lunchKanban = createView({
|
||||
var lunchKanban = await createView({
|
||||
View: LunchKanbanView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -36,7 +36,7 @@ QUnit.module('Views', {
|
||||
'</t></templates></kanban>',
|
||||
mockRPC: function(route, args) {
|
||||
if (route === '/lunch/infos') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
order: 1,
|
||||
wallet: 20,
|
||||
username: 'Marc Demo',
|
||||
@@ -56,11 +56,11 @@ QUnit.module('Views', {
|
||||
user_location: [1, 'hello'],
|
||||
});
|
||||
} else if (route === '/lunch/user_location_get') {
|
||||
return $.when(1);
|
||||
return Promise.resolve(1);
|
||||
} else if (route === '/web/dataset/call_kw/ir.model.data/xmlid_to_res_id') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
} else if (route.startsWith('data:image/png;base64,')) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -88,12 +88,12 @@ QUnit.module('Views', {
|
||||
lunchKanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('User interactions', function (assert) {
|
||||
QUnit.test('User interactions', async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
var state = 'new';
|
||||
|
||||
var lunchKanban = createView({
|
||||
var lunchKanban = await createView({
|
||||
View: LunchKanbanView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -103,7 +103,7 @@ QUnit.module('Views', {
|
||||
'</t></templates></kanban>',
|
||||
mockRPC: function(route, args) {
|
||||
if (route === '/lunch/infos') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
order: 1,
|
||||
wallet: 20,
|
||||
username: 'Marc Demo',
|
||||
@@ -124,35 +124,35 @@ QUnit.module('Views', {
|
||||
user_location: [1, 'hello'],
|
||||
});
|
||||
} else if (route === '/lunch/payment_message') {
|
||||
return $.when({message: 'Hello'});
|
||||
return Promise.resolve({message: 'Hello'});
|
||||
} else if (route === '/lunch/pay') {
|
||||
return $.when(true);
|
||||
return Promise.resolve(true);
|
||||
} else if (route === '/lunch/user_location_get') {
|
||||
return $.when(1);
|
||||
return Promise.resolve(1);
|
||||
} else if (args.method === 'update_quantity') {
|
||||
assert.step(args.args);
|
||||
return $.when();
|
||||
assert.step(JSON.stringify(args.args));
|
||||
return Promise.resolve();
|
||||
} else if (route === '/web/dataset/call_kw/ir.model.data/xmlid_to_res_id') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
} else if (route.startsWith('data:image/png;base64,')) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
|
||||
lunchKanban.$('.o_add_product').click();
|
||||
lunchKanban.$('.o_remove_product').click();
|
||||
await testUtils.dom.click(lunchKanban.$('.o_add_product'));
|
||||
await testUtils.dom.click(lunchKanban.$('.o_remove_product'));
|
||||
|
||||
state = 'ordered';
|
||||
lunchKanban.$('.o_lunch_widget_order_button').click();
|
||||
await testUtils.dom.click(lunchKanban.$('.o_lunch_widget_order_button'));
|
||||
// state is shown
|
||||
assert.strictEqual(lunchKanban.$('.o_lunch_ordered').length, 1, 'state should be shown as ordered');
|
||||
// buttons
|
||||
assert.strictEqual(lunchKanban.$('.o_remove_product').length, 1, 'button to remove product should be shown');
|
||||
assert.strictEqual(lunchKanban.$('.o_add_product').length, 1, 'button to add product should be shown');
|
||||
state = 'confirmed';
|
||||
lunchKanban.reload();
|
||||
await lunchKanban.reload();
|
||||
// state is updated
|
||||
assert.strictEqual(lunchKanban.$('.o_lunch_confirmed').length, 1, 'state should be shown as confirmed');
|
||||
// Buttons not shown anymore
|
||||
@@ -161,17 +161,17 @@ QUnit.module('Views', {
|
||||
|
||||
|
||||
assert.verifySteps([
|
||||
[[1], 1],
|
||||
[[1], -1],
|
||||
JSON.stringify([[1], 1]),
|
||||
JSON.stringify([[1], -1]),
|
||||
]);
|
||||
|
||||
lunchKanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Manager interactions', function (assert) {
|
||||
QUnit.test('Manager interactions', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var lunchKanban = createView({
|
||||
var lunchKanban = await createView({
|
||||
View: LunchKanbanView,
|
||||
model: 'product',
|
||||
data: this.data,
|
||||
@@ -181,7 +181,7 @@ QUnit.module('Views', {
|
||||
'</t></templates></kanban>',
|
||||
mockRPC: function(route, args) {
|
||||
if (route === '/lunch/infos') {
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
order: 1,
|
||||
wallet: 20,
|
||||
username: 'Marc Demo',
|
||||
@@ -202,11 +202,11 @@ QUnit.module('Views', {
|
||||
user_location: [1, 'hello'],
|
||||
});
|
||||
} else if (route === '/lunch/user_location_get') {
|
||||
return $.when(1);
|
||||
return Promise.resolve(1);
|
||||
} else if (route === '/web/dataset/call_kw/ir.model.data/xmlid_to_res_id') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
} else if (route.startsWith('data:image/png;base64,')) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
|
||||
@@ -25,11 +25,11 @@ var _t = core._t;
|
||||
*
|
||||
* @param {Widget} self a widget instance that can perform RPCs
|
||||
* @param {Array} ids the ids of activities to read
|
||||
* @return {Deferred<Array>} resolved with the activities
|
||||
* @return {Promise<Array>} resolved with the activities
|
||||
*/
|
||||
function _readActivities(self, ids) {
|
||||
if (!ids.length) {
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
var context = self.getSession().user_context;
|
||||
if (self.record && !_.isEmpty(self.record.getContext())) {
|
||||
@@ -64,7 +64,7 @@ BasicModel.include({
|
||||
* @private
|
||||
* @param {Object} record - an element from the localData
|
||||
* @param {string} fieldName
|
||||
* @return {Deferred<Array>} resolved with the activities
|
||||
* @return {Promise<Array>} resolved with the activities
|
||||
*/
|
||||
_fetchSpecialActivity: function (record, fieldName) {
|
||||
var localID = (record._changes && fieldName in record._changes) ?
|
||||
@@ -144,7 +144,7 @@ var BasicActivity = AbstractField.extend({
|
||||
|
||||
/**
|
||||
* @param {integer} previousActivityTypeID
|
||||
* @return {$.Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
scheduleActivity: function () {
|
||||
var callback = this._reload.bind(this, { activity: true, thread: true });
|
||||
@@ -213,7 +213,7 @@ var BasicActivity = AbstractField.extend({
|
||||
* @param {integer} id
|
||||
* @param {integer} previousActivityTypeID
|
||||
* @param {function} callback
|
||||
* @return {$.Deferred}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_openActivityForm: function (id, callback) {
|
||||
var action = {
|
||||
@@ -237,7 +237,7 @@ var BasicActivity = AbstractField.extend({
|
||||
* @param {integer} activityID
|
||||
* @param {string} feedback
|
||||
* @param {integer[]} attachmentIds
|
||||
* @return {$.Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_sendActivityFeedback: function (activityID, feedback, attachmentIds) {
|
||||
return this._rpc({
|
||||
@@ -303,7 +303,7 @@ var BasicActivity = AbstractField.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onEditActivity: function (ev) {
|
||||
ev.preventDefault();
|
||||
@@ -449,7 +449,7 @@ var BasicActivity = AbstractField.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
* @returns {$.Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onPreviewMailTemplate: function (ev) {
|
||||
ev.stopPropagation();
|
||||
@@ -477,7 +477,7 @@ var BasicActivity = AbstractField.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onSendMailTemplate: function (ev) {
|
||||
ev.stopPropagation();
|
||||
@@ -493,7 +493,7 @@ var BasicActivity = AbstractField.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
* @returns {$.Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onScheduleActivity: function (ev) {
|
||||
ev.preventDefault();
|
||||
@@ -504,7 +504,7 @@ var BasicActivity = AbstractField.extend({
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
* @param {Object} options
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onUnlinkActivity: function (ev, options) {
|
||||
ev.preventDefault();
|
||||
|
||||
@@ -103,7 +103,7 @@ var Chatter = Widget.extend({
|
||||
}));
|
||||
// start and append the widgets
|
||||
var fieldDefs = _.invoke(this.fields, 'appendTo', $('<div>'));
|
||||
var def = this._dp.add($.when.apply($, fieldDefs));
|
||||
var def = this._dp.add(Promise.all(fieldDefs));
|
||||
this._render(def).then(this._updateMentionSuggestions.bind(this));
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
@@ -153,7 +153,7 @@ var Chatter = Widget.extend({
|
||||
fieldsToReset = this.fields;
|
||||
}
|
||||
var fieldDefs = _.invoke(fieldsToReset, 'reset', record);
|
||||
var def = this._dp.add($.when.apply($, fieldDefs));
|
||||
var def = this._dp.add(Promise.all(fieldDefs));
|
||||
this._render(def).then(function () {
|
||||
self.$el.height('auto');
|
||||
self._updateMentionSuggestions();
|
||||
@@ -203,17 +203,18 @@ var Chatter = Widget.extend({
|
||||
* Discard changes on the record.
|
||||
*
|
||||
* @private
|
||||
* @returns {$.Deferred} resolved if successfully discarding changes on
|
||||
* @returns {Promise} resolved if successfully discarding changes on
|
||||
* the record, rejected otherwise
|
||||
*/
|
||||
_discardChanges: function () {
|
||||
var def = $.Deferred();
|
||||
this.trigger_up('discard_changes', {
|
||||
recordID: this.record.id,
|
||||
onSuccess: def.resolve.bind(def),
|
||||
onFailure: def.reject.bind(def),
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.trigger_up('discard_changes', {
|
||||
recordID: self.record.id,
|
||||
onSuccess: resolve,
|
||||
onFailure: reject,
|
||||
});
|
||||
});
|
||||
return def;
|
||||
},
|
||||
/**
|
||||
* Discard changes on the record if the message will reload the record
|
||||
@@ -221,14 +222,14 @@ var Chatter = Widget.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {Object} messageData
|
||||
* @return {$.Deferred} resolved if no reload or proceed to discard the
|
||||
* @return {Promise} resolved if no reload or proceed to discard the
|
||||
* changes on the record, rejected otherwise
|
||||
*/
|
||||
_discardOnReload: function (messageData) {
|
||||
if (this._reloadAfterPost(messageData)) {
|
||||
return this._discardChanges();
|
||||
}
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -323,7 +324,7 @@ var Chatter = Widget.extend({
|
||||
self._reloadAttachmentBox();
|
||||
self.trigger_up('reload', {fieldNames: ['message_attachment_count'], keepChanges: true});
|
||||
}
|
||||
}).fail(function () {
|
||||
}).guardedCatch(function () {
|
||||
self._enableComposer();
|
||||
});
|
||||
});
|
||||
@@ -375,9 +376,9 @@ var Chatter = Widget.extend({
|
||||
_render: function (def) {
|
||||
// the rendering of the chatter is aynchronous: relational data of its fields needs to be
|
||||
// fetched (in some case, it might be synchronous as they hold an internal cache).
|
||||
// this function takes a deferred as argument, which is resolved once all fields have
|
||||
// this function takes a promise as argument, which is resolved once all fields have
|
||||
// fetched their data
|
||||
// this function appends the fields where they should be once the given deferred is resolved
|
||||
// this function appends the fields where they should be once the given promise is resolved
|
||||
// and if it takes more than 500ms, displays a spinner to indicate that it is loading
|
||||
var self = this;
|
||||
|
||||
@@ -385,6 +386,11 @@ var Chatter = Widget.extend({
|
||||
concurrency.rejectAfter(concurrency.delay(500), def).then(function () {
|
||||
$spinner.appendTo(self.$el);
|
||||
});
|
||||
var always = function () {
|
||||
// disable widgets in create mode, otherwise enable
|
||||
self._isCreateMode ? self._disableChatter() : self._enableChatter();
|
||||
$spinner.remove();
|
||||
};
|
||||
|
||||
return def.then(function () {
|
||||
if (self.fields.activity) {
|
||||
@@ -400,11 +406,7 @@ var Chatter = Widget.extend({
|
||||
if (self.fields.thread) {
|
||||
self.fields.thread.$el.appendTo(self.$el);
|
||||
}
|
||||
}).always(function () {
|
||||
// disable widgets in create mode, otherwise enable
|
||||
self._isCreateMode ? self._disableChatter() : self._enableChatter();
|
||||
$spinner.remove();
|
||||
});
|
||||
}).then(always).guardedCatch(always);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -517,7 +519,7 @@ var Chatter = Widget.extend({
|
||||
if (!this._areAttachmentsLoaded) {
|
||||
def = this._fetchAttachments();
|
||||
}
|
||||
$.when(def).then(this._openAttachmentBox.bind(this));
|
||||
Promise.resolve(def).then(this._openAttachmentBox.bind(this));
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -534,10 +536,10 @@ var Chatter = Widget.extend({
|
||||
_onOpenComposerMessage: function () {
|
||||
var self = this;
|
||||
if (!this.suggested_partners_def) {
|
||||
this.suggested_partners_def = $.Deferred();
|
||||
var method = 'message_get_suggested_recipients';
|
||||
var args = [[this.context.default_res_id], this.context];
|
||||
this._rpc({model: this.record.model, method: method, args: args})
|
||||
this.suggested_partners_def = new Promise(function (resolve, reject) {
|
||||
var method = 'message_get_suggested_recipients';
|
||||
var args = [[self.context.default_res_id], self.context];
|
||||
self._rpc({model: self.record.model, method: method, args: args})
|
||||
.then(function (result) {
|
||||
if (!self.suggested_partners_def) {
|
||||
return; // widget has been reset (e.g. we just switched to another record)
|
||||
@@ -555,8 +557,9 @@ var Chatter = Widget.extend({
|
||||
reason: recipient[2],
|
||||
});
|
||||
});
|
||||
self.suggested_partners_def.resolve(suggested_partners);
|
||||
resolve(suggested_partners);
|
||||
});
|
||||
});
|
||||
}
|
||||
this.suggested_partners_def.then(function (suggested_partners) {
|
||||
self._openComposer({ isLog: false, suggested_partners: suggested_partners });
|
||||
|
||||
@@ -128,7 +128,7 @@ var BasicComposer = Widget.extend({
|
||||
.on('update_typing_partners', this, this._onUpdateTypingPartners);
|
||||
|
||||
// Mention
|
||||
this._mentionManager.prependTo(this.$('.o_composer'));
|
||||
var prependPromise = this._mentionManager.prependTo(this.$('.o_composer'));
|
||||
|
||||
// Drag-Drop files
|
||||
// Allowing body to detect dragenter and dragleave for display
|
||||
@@ -137,7 +137,7 @@ var BasicComposer = Widget.extend({
|
||||
$body.on('dragleave.' + this._dropZoneNS, this._onBodyFileDragLeave.bind(this));
|
||||
$body.on("dragover." + this._dropZoneNS, this._onBodyFileDragover.bind(this));
|
||||
$body.on("drop." + this._dropZoneNS, this._onBodyFileDrop.bind(this));
|
||||
return this._super();
|
||||
return Promise.all([this._super(), prependPromise]);
|
||||
},
|
||||
destroy: function () {
|
||||
$("body").off('dragleave.' + this._dropZoneNS);
|
||||
@@ -236,7 +236,7 @@ var BasicComposer = Widget.extend({
|
||||
* displayed to the user. If none of them match, then it will fetch for more
|
||||
* partner suggestions (@see _mentionFetchPartners).
|
||||
*
|
||||
* @param {$.Deferred<Object[]>} prefetchedPartners list of list of
|
||||
* @param {Promise<Object[]>} prefetchedPartners list of list of
|
||||
* prefetched partners.
|
||||
*/
|
||||
mentionSetPrefetchedPartners: function (prefetchedPartners) {
|
||||
@@ -299,20 +299,21 @@ var BasicComposer = Widget.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {string} search
|
||||
* @returns {$.Deferred<Object[]>}
|
||||
* @returns {Promise<Object[]>}
|
||||
*/
|
||||
_mentionGetCannedResponses: function (search) {
|
||||
var self = this;
|
||||
var def = $.Deferred();
|
||||
clearTimeout(this._cannedTimeout);
|
||||
this._cannedTimeout = setTimeout(function () {
|
||||
var cannedResponses = self.call('mail_service', 'getCannedResponses');
|
||||
var matches = fuzzy.filter(utils.unaccent(search), _.pluck(cannedResponses, 'source'));
|
||||
var indexes = _.pluck(matches.slice(0, self.options.mentionFetchLimit), 'index');
|
||||
def.resolve(_.map(indexes, function (index) {
|
||||
return cannedResponses[index];
|
||||
}));
|
||||
}, 500);
|
||||
var def = new Promise(function (resolve, reject) {
|
||||
clearTimeout(self._cannedTimeout);
|
||||
self._cannedTimeout = setTimeout(function () {
|
||||
var cannedResponses = self.call('mail_service', 'getCannedResponses');
|
||||
var matches = fuzzy.filter(utils.unaccent(search), _.pluck(cannedResponses, 'source'));
|
||||
var indexes = _.pluck(matches.slice(0, self.options.mentionFetchLimit), 'index');
|
||||
resolve(_.map(indexes, function (index) {
|
||||
return cannedResponses[index];
|
||||
}));
|
||||
}, 500);
|
||||
});
|
||||
return def;
|
||||
},
|
||||
/**
|
||||
@@ -346,7 +347,7 @@ var BasicComposer = Widget.extend({
|
||||
*/
|
||||
_mentionFetchPartners: function (search) {
|
||||
var self = this;
|
||||
return $.when(this._mentionPrefetchedPartners).then(function (prefetchedPartners) {
|
||||
return Promise.resolve(this._mentionPrefetchedPartners).then(function (prefetchedPartners) {
|
||||
// filter prefetched partners with the given search string
|
||||
var suggestions = [];
|
||||
var limit = self.options.mentionFetchLimit;
|
||||
@@ -371,7 +372,7 @@ var BasicComposer = Widget.extend({
|
||||
{ limit: limit, search: search }
|
||||
);
|
||||
}
|
||||
return $.when(suggestions).then(function (suggestions) {
|
||||
return Promise.resolve(suggestions).then(function (suggestions) {
|
||||
//add im_status on suggestions
|
||||
_.each(suggestions, function (suggestionsSet) {
|
||||
_.each(suggestionsSet, function (suggestion) {
|
||||
@@ -387,27 +388,27 @@ var BasicComposer = Widget.extend({
|
||||
* @param {string} model
|
||||
* @param {string} method
|
||||
* @param {Object} kwargs
|
||||
* @return {$.Deferred}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_mentionFetchThrottled: function (model, method, kwargs) {
|
||||
var self = this;
|
||||
// Delays the execution of the RPC to prevent unnecessary RPCs when the user is still typing
|
||||
var def = $.Deferred();
|
||||
clearTimeout(this.mentionFetchTimer);
|
||||
this.mentionFetchTimer = setTimeout(function () {
|
||||
return self._rpc({model: model, method: method, kwargs: kwargs})
|
||||
return new Promise(function (resolve, reject) {
|
||||
clearTimeout(self.mentionFetchTimer);
|
||||
self.mentionFetchTimer = setTimeout(function () {
|
||||
return self._rpc({model: model, method: method, kwargs: kwargs})
|
||||
.then(function (results) {
|
||||
def.resolve(results);
|
||||
resolve(results);
|
||||
});
|
||||
}, this.MENTION_THROTTLE);
|
||||
return def;
|
||||
}, self.MENTION_THROTTLE);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {$.Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_preprocessMessage: function () {
|
||||
// Return a deferred as this function is extended with asynchronous
|
||||
// Return a promise as this function is extended with asynchronous
|
||||
// behavior for the chatter composer
|
||||
|
||||
//Removing unwanted extra spaces from message
|
||||
@@ -420,7 +421,7 @@ var BasicComposer = Widget.extend({
|
||||
var commands = this.options.commandsEnabled ?
|
||||
this._mentionManager.getListenerSelection('/') :
|
||||
[];
|
||||
return $.when({
|
||||
return Promise.resolve({
|
||||
content: this._mentionManager.generateLinks(value),
|
||||
attachment_ids: _.pluck(this.get('attachment_ids'), 'id'),
|
||||
partner_ids: _.uniq(_.pluck(this._mentionManager.getListenerSelection('@'), 'id')),
|
||||
|
||||
@@ -59,109 +59,112 @@ var ChatterComposer = BasicComposer.extend({
|
||||
* @private
|
||||
* @param {Array} checkedSuggestedPartners list of 'recipient' partners to
|
||||
* complete informations or validate
|
||||
* @returns {Deferred} resolved with the list of checked suggested partners
|
||||
* @returns {Promise} resolved with the list of checked suggested partners
|
||||
* (real partner)
|
||||
**/
|
||||
_checkSuggestedPartners: function (checkedSuggestedPartners) {
|
||||
var self = this;
|
||||
var checkDone = $.Deferred();
|
||||
|
||||
var recipients = _.filter(checkedSuggestedPartners, function (recipient) {
|
||||
return recipient.checked;
|
||||
});
|
||||
var recipientsToFind = _.filter(recipients, function (recipient) {
|
||||
return (! recipient.partner_id);
|
||||
});
|
||||
var namesToFind = _.pluck(recipientsToFind, 'full_name');
|
||||
var recipientsToCheck = _.filter(recipients, function (recipient) {
|
||||
return (recipient.partner_id && ! recipient.email_address);
|
||||
});
|
||||
var recipientIDs = _.pluck(_.filter(recipients, function (recipient) {
|
||||
return recipient.partner_id && recipient.email_address;
|
||||
}), 'partner_id');
|
||||
|
||||
var namesToRemove = [];
|
||||
var recipientIDsToRemove = [];
|
||||
|
||||
// have unknown names
|
||||
// -> call message_get_partner_info_from_emails to try to find
|
||||
// partner_id
|
||||
var def;
|
||||
if (namesToFind.length > 0) {
|
||||
def = this._rpc({
|
||||
model: this._model,
|
||||
method: 'message_partner_info_from_emails',
|
||||
args: [[this.context.default_res_id], namesToFind],
|
||||
});
|
||||
}
|
||||
|
||||
// for unknown names + incomplete partners
|
||||
// -> open popup - cancel = remove from recipients
|
||||
$.when(def).pipe(function (result) {
|
||||
result = result || [];
|
||||
var emailDefs = [];
|
||||
var recipientPopups = result.concat(recipientsToCheck);
|
||||
|
||||
_.each(recipientPopups, function (partnerInfo) {
|
||||
var deferred = $.Deferred();
|
||||
emailDefs.push(deferred);
|
||||
|
||||
var partnerName = partnerInfo.full_name;
|
||||
var partnerID = partnerInfo.partner_id;
|
||||
var parsedEmail = mailUtils.parseEmail(partnerName);
|
||||
|
||||
var dialog = new viewDialogs.FormViewDialog(self, {
|
||||
res_model: 'res.partner',
|
||||
res_id: partnerID,
|
||||
context: {
|
||||
active_model: self._model,
|
||||
active_id: self.context.default_res_id,
|
||||
force_email: true,
|
||||
ref: 'compound_context',
|
||||
default_name: parsedEmail[0],
|
||||
default_email: parsedEmail[1],
|
||||
},
|
||||
title: _t("Please complete customer's informations"),
|
||||
disable_multiple_selection: true,
|
||||
}).open();
|
||||
dialog.on('closed', self, function () {
|
||||
deferred.resolve();
|
||||
});
|
||||
dialog.opened().then(function () {
|
||||
dialog.form_view.on('on_button_cancel', self, function () {
|
||||
namesToRemove.push(partnerName);
|
||||
if (partnerID) {
|
||||
recipientIDsToRemove.push(partnerID);
|
||||
}
|
||||
});
|
||||
});
|
||||
var checkDone = new Promise(function (resolve, reject) {
|
||||
var recipients = _.filter(checkedSuggestedPartners, function (recipient) {
|
||||
return recipient.checked;
|
||||
});
|
||||
$.when.apply($, emailDefs).then(function () {
|
||||
var newNamesToFind = _.difference(namesToFind, namesToRemove);
|
||||
var def;
|
||||
if (newNamesToFind.length > 0) {
|
||||
def = self._rpc({
|
||||
model: self._model,
|
||||
method: 'message_partner_info_from_emails',
|
||||
args: [[self.context.default_res_id], newNamesToFind, true],
|
||||
var recipientsToFind = _.filter(recipients, function (recipient) {
|
||||
return (! recipient.partner_id);
|
||||
});
|
||||
var namesToFind = _.pluck(recipientsToFind, 'full_name');
|
||||
var recipientsToCheck = _.filter(recipients, function (recipient) {
|
||||
return (recipient.partner_id && ! recipient.email_address);
|
||||
});
|
||||
var recipientIDs = _.pluck(_.filter(recipients, function (recipient) {
|
||||
return recipient.partner_id && recipient.email_address;
|
||||
}), 'partner_id');
|
||||
|
||||
var namesToRemove = [];
|
||||
var recipientIDsToRemove = [];
|
||||
|
||||
// have unknown names
|
||||
// -> call message_get_partner_info_from_emails to try to find
|
||||
// partner_id
|
||||
var def;
|
||||
if (namesToFind.length > 0) {
|
||||
def = self._rpc({
|
||||
model: self._model,
|
||||
method: 'message_partner_info_from_emails',
|
||||
args: [[self.context.default_res_id], namesToFind],
|
||||
});
|
||||
}
|
||||
|
||||
// for unknown names + incomplete partners
|
||||
// -> open popup - cancel = remove from recipients
|
||||
Promise.resolve(def).then(function (result) {
|
||||
result = result || [];
|
||||
var emailDefs = [];
|
||||
var recipientPopups = result.concat(recipientsToCheck);
|
||||
|
||||
_.each(recipientPopups, function (partnerInfo) {
|
||||
var prom = new Promise(function(innerResolve, innerReject) {
|
||||
var partnerName = partnerInfo.full_name;
|
||||
var partnerID = partnerInfo.partner_id;
|
||||
var parsedEmail = mailUtils.parseEmail(partnerName);
|
||||
|
||||
var dialog = new viewDialogs.FormViewDialog(self, {
|
||||
res_model: 'res.partner',
|
||||
res_id: partnerID,
|
||||
context: {
|
||||
active_model: self._model,
|
||||
active_id: self.context.default_res_id,
|
||||
force_email: true,
|
||||
ref: 'compound_context',
|
||||
default_name: parsedEmail[0],
|
||||
default_email: parsedEmail[1],
|
||||
},
|
||||
title: _t("Please complete customer's informations"),
|
||||
disable_multiple_selection: true,
|
||||
}).open();
|
||||
dialog.on('closed', self, function () {
|
||||
innerResolve();
|
||||
});
|
||||
dialog.opened().then(function () {
|
||||
dialog.form_view.on('on_button_cancel', self, function () {
|
||||
namesToRemove.push(partnerName);
|
||||
if (partnerID) {
|
||||
recipientIDsToRemove.push(partnerID);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
$.when(def).pipe(function (result) {
|
||||
result = result || [];
|
||||
var recipientPopups = result.concat(recipientsToCheck);
|
||||
_.each(recipientPopups, function (partnerInfo) {
|
||||
if (
|
||||
partnerInfo.partner_id &&
|
||||
_.indexOf(partnerInfo.partner_id, recipientIDsToRemove) === -1
|
||||
) {
|
||||
recipientIDs.push(partnerInfo.partner_id);
|
||||
}
|
||||
});
|
||||
}).pipe(function () {
|
||||
checkDone.resolve(recipientIDs);
|
||||
|
||||
emailDefs.push(prom);
|
||||
|
||||
});
|
||||
Promise.all(emailDefs).then(function () {
|
||||
var newNamesToFind = _.difference(namesToFind, namesToRemove);
|
||||
var def;
|
||||
if (newNamesToFind.length > 0) {
|
||||
def = self._rpc({
|
||||
model: self._model,
|
||||
method: 'message_partner_info_from_emails',
|
||||
args: [[self.context.default_res_id], newNamesToFind, true],
|
||||
});
|
||||
}
|
||||
Promise.resolve(def).then(function (result) {
|
||||
result = result || [];
|
||||
var recipientPopups = result.concat(recipientsToCheck);
|
||||
_.each(recipientPopups, function (partnerInfo) {
|
||||
if (
|
||||
partnerInfo.partner_id &&
|
||||
_.indexOf(partnerInfo.partner_id, recipientIDsToRemove) === -1
|
||||
) {
|
||||
recipientIDs.push(partnerInfo.partner_id);
|
||||
}
|
||||
});
|
||||
}).then(function () {
|
||||
resolve(recipientIDs);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return checkDone;
|
||||
},
|
||||
/**
|
||||
@@ -187,41 +190,40 @@ var ChatterComposer = BasicComposer.extend({
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
* @returns {$.Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_preprocessMessage: function () {
|
||||
var self = this;
|
||||
var def = $.Deferred();
|
||||
this._super().then(function (message) {
|
||||
message = _.extend(message, {
|
||||
subtype: 'mail.mt_comment',
|
||||
message_type: 'comment',
|
||||
context: _.defaults({}, self.context, session.user_context),
|
||||
});
|
||||
|
||||
// Subtype
|
||||
if (self.options.isLog) {
|
||||
message.subtype = 'mail.mt_note';
|
||||
}
|
||||
|
||||
// Partner_ids
|
||||
if (!self.options.isLog) {
|
||||
var checkedSuggestedPartners = self._getCheckedSuggestedPartners();
|
||||
self._checkSuggestedPartners(checkedSuggestedPartners).done(function (partnerIDs) {
|
||||
message.partner_ids = (message.partner_ids || []).concat(partnerIDs);
|
||||
// update context
|
||||
message.context = _.defaults({}, message.context, {
|
||||
mail_post_autofollow: true,
|
||||
});
|
||||
def.resolve(message);
|
||||
return new Promise(function (resolve, reject) {
|
||||
self._super().then(function (message) {
|
||||
message = _.extend(message, {
|
||||
subtype: 'mail.mt_comment',
|
||||
message_type: 'comment',
|
||||
context: _.defaults({}, self.context, session.user_context),
|
||||
});
|
||||
} else {
|
||||
def.resolve(message);
|
||||
}
|
||||
|
||||
// Subtype
|
||||
if (self.options.isLog) {
|
||||
message.subtype = 'mail.mt_note';
|
||||
}
|
||||
|
||||
// Partner_ids
|
||||
if (!self.options.isLog) {
|
||||
var checkedSuggestedPartners = self._getCheckedSuggestedPartners();
|
||||
self._checkSuggestedPartners(checkedSuggestedPartners).then(function (partnerIDs) {
|
||||
message.partner_ids = (message.partner_ids || []).concat(partnerIDs);
|
||||
// update context
|
||||
message.context = _.defaults({}, message.context, {
|
||||
mail_post_autofollow: true,
|
||||
});
|
||||
resolve(message);
|
||||
});
|
||||
} else {
|
||||
resolve(message);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
return def;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -244,20 +246,20 @@ var ChatterComposer = BasicComposer.extend({
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var recipientDoneDef = $.Deferred();
|
||||
|
||||
// any operation on the full-composer will reload the record, so
|
||||
// warn the user that any unsaved changes on the record will be lost.
|
||||
this.trigger_up('discard_record_changes', {
|
||||
proceed: function () {
|
||||
if (self.options.isLog) {
|
||||
recipientDoneDef.resolve([]);
|
||||
} else {
|
||||
var checkedSuggestedPartners = self._getCheckedSuggestedPartners();
|
||||
self._checkSuggestedPartners(checkedSuggestedPartners)
|
||||
.then(recipientDoneDef.resolve.bind(recipientDoneDef));
|
||||
}
|
||||
},
|
||||
var recipientDoneDef = new Promise(function (resolve, reject) {
|
||||
// any operation on the full-composer will reload the record, so
|
||||
// warn the user that any unsaved changes on the record will be lost.
|
||||
self.trigger_up('discard_record_changes', {
|
||||
proceed: function () {
|
||||
if (self.options.isLog) {
|
||||
resolve([]);
|
||||
} else {
|
||||
var checkedSuggestedPartners = self._getCheckedSuggestedPartners();
|
||||
self._checkSuggestedPartners(checkedSuggestedPartners)
|
||||
.then(resolve);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
recipientDoneDef.then(function (partnerIDs) {
|
||||
|
||||
@@ -102,7 +102,7 @@ var ExtendedComposer = BasicComposer.extend({
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
* @returns {$.Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_preprocessMessage: function () {
|
||||
var self = this;
|
||||
|
||||
@@ -114,7 +114,7 @@ var MentionManager = Widget.extend({
|
||||
|
||||
if (this._activeListener) {
|
||||
var mentionWord = this._mentionWord;
|
||||
$.when(this._activeListener.fetchCallback(mentionWord))
|
||||
Promise.resolve(this._activeListener.fetchCallback(mentionWord))
|
||||
.then(function (suggestions) {
|
||||
if (mentionWord === self._mentionWord) {
|
||||
// update suggestions only if mentionWord didn't change
|
||||
|
||||
@@ -82,7 +82,7 @@ var PartnerInviteDialog = Dialog.extend({
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_addChannel: function () {
|
||||
var self = this;
|
||||
@@ -297,7 +297,7 @@ var Discuss = AbstractAction.extend({
|
||||
* @override
|
||||
*/
|
||||
willStart: function () {
|
||||
return $.when(this._super(), this.call('mail_service', 'isReady'));
|
||||
return Promise.all([this._super(), this.call('mail_service', 'isReady')]);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -331,7 +331,7 @@ var Discuss = AbstractAction.extend({
|
||||
this._extendedComposer.appendTo(this.$('.o_mail_discuss_content')));
|
||||
defs.push(this._super.apply(this, arguments));
|
||||
|
||||
return $.when.apply($, defs)
|
||||
return Promise.all(defs)
|
||||
.then(function () {
|
||||
return self._setThread(self._defaultThreadID);
|
||||
})
|
||||
@@ -474,7 +474,7 @@ var Discuss = AbstractAction.extend({
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_fetchAndRenderThread: function () {
|
||||
var self = this;
|
||||
@@ -567,7 +567,7 @@ var Discuss = AbstractAction.extend({
|
||||
* loaded when scrolling to the top, so they can't be loaded if there is no
|
||||
* scrollbar)
|
||||
*
|
||||
* @returns {Deferred} resolved when there are enough messages to fill the
|
||||
* @returns {Promise} resolved when there are enough messages to fill the
|
||||
* screen, or when there is no more message to fetch
|
||||
*/
|
||||
_loadEnoughMessages: function () {
|
||||
@@ -583,7 +583,7 @@ var Discuss = AbstractAction.extend({
|
||||
* Load more messages for the current thread
|
||||
*
|
||||
* @private
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_loadMoreMessages: function () {
|
||||
var self = this;
|
||||
@@ -647,7 +647,7 @@ var Discuss = AbstractAction.extend({
|
||||
autoFocus: true,
|
||||
source: function (request, response) {
|
||||
self._lastSearchVal = _.escape(request.term);
|
||||
self._searchChannel(self._lastSearchVal).done(function (result){
|
||||
self._searchChannel(self._lastSearchVal).then(function (result){
|
||||
result.push({
|
||||
label: _.str.sprintf(
|
||||
'<strong>' + _t("Create %s (Public)") + '</strong>',
|
||||
@@ -689,7 +689,7 @@ var Discuss = AbstractAction.extend({
|
||||
autoFocus: true,
|
||||
source: function (request, response) {
|
||||
self._lastSearchVal = _.escape(request.term);
|
||||
self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).done(response);
|
||||
self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).then(response);
|
||||
},
|
||||
select: function (ev, ui) {
|
||||
var partnerID = ui.item.id;
|
||||
@@ -819,7 +819,7 @@ var Discuss = AbstractAction.extend({
|
||||
* Renders, binds events and appends a thread widget.
|
||||
*
|
||||
* @private
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_renderThread: function () {
|
||||
this._threadWidget = new ThreadWidget(this, {
|
||||
@@ -877,7 +877,7 @@ var Discuss = AbstractAction.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {string} searchVal
|
||||
* @returns {$.Promise<Array>}
|
||||
* @returns {Promise<Array>}
|
||||
*/
|
||||
_searchChannel: function (searchVal){
|
||||
return this._rpc({
|
||||
@@ -915,7 +915,7 @@ var Discuss = AbstractAction.extend({
|
||||
this._extendedComposer.do_show();
|
||||
|
||||
this._threadWidget.scrollToMessage({
|
||||
msgID: messageID,
|
||||
messageID: messageID,
|
||||
duration: 200,
|
||||
onlyIfNecessary: true
|
||||
});
|
||||
@@ -927,7 +927,7 @@ var Discuss = AbstractAction.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {integer} threadID a thread with such ID
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_setThread: function (threadID) {
|
||||
var self = this;
|
||||
@@ -1483,9 +1483,6 @@ var Discuss = AbstractAction.extend({
|
||||
} else {
|
||||
self._threadWidget.scrollToBottom();
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
// todo: display notifications
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -116,7 +116,7 @@ Discuss.include({
|
||||
this._thread = thread;
|
||||
if (thread.getType() !== 'mailbox') {
|
||||
this.call('mail_service', 'openThreadWindow', threadID);
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ Discuss.include({
|
||||
});
|
||||
def = this.call('mail_service', 'getChannelPreviews', channels);
|
||||
}
|
||||
return $.when(def).then(function (previews) {
|
||||
return Promise.resolve(def).then(function (previews) {
|
||||
// update content
|
||||
if (inMailbox) {
|
||||
if (!previouslyInInbox) {
|
||||
|
||||
@@ -76,7 +76,7 @@ var DocumentViewer = Widget.extend({
|
||||
start: function () {
|
||||
this.$el.modal('show');
|
||||
this.$el.on('hidden.bs.modal', _.bind(this._onDestroy, this));
|
||||
this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this));
|
||||
this.$('.o_viewer_img').on("load", _.bind(this._onImageLoaded, this));
|
||||
this.$('[data-toggle="tooltip"]').tooltip({delay: 0});
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -132,7 +132,7 @@ var DocumentViewer = Widget.extend({
|
||||
this.$('.o_viewer_content').html(QWeb.render('DocumentViewer.Content', {
|
||||
widget: this
|
||||
}));
|
||||
this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this));
|
||||
this.$('.o_viewer_img').on("load", _.bind(this._onImageLoaded, this));
|
||||
this.$('[data-toggle="tooltip"]').tooltip({delay: 0});
|
||||
this._reset();
|
||||
},
|
||||
|
||||
@@ -55,14 +55,14 @@ var Followers = AbstractField.extend({
|
||||
// note: the rendering of this widget is asynchronous as it needs to
|
||||
// fetch the details of the followers, but it performs a first rendering
|
||||
// synchronously (_displayGeneric), and updates its rendering once it
|
||||
// has fetched the required data, so this function doesn't return a deferred
|
||||
// has fetched the required data, so this function doesn't return a promise
|
||||
// as we don't want to wait to the data to be loaded to display the widget
|
||||
var self = this;
|
||||
var fetch_def = this.dp.add(this._readFollowers());
|
||||
|
||||
concurrency.rejectAfter(concurrency.delay(0), fetch_def).then(this._displayGeneric.bind(this));
|
||||
|
||||
fetch_def.then(function () {
|
||||
return fetch_def.then(function () {
|
||||
self._displayButtons();
|
||||
self._displayFollowers(self.followers);
|
||||
if (self.subtypes) { // current user is follower
|
||||
@@ -203,7 +203,7 @@ var Followers = AbstractField.extend({
|
||||
params: { follower_ids: missing_ids, res_model: this.model }
|
||||
});
|
||||
}
|
||||
return $.when(def).then(function (results) {
|
||||
return Promise.resolve(def).then(function (results) {
|
||||
if (results) {
|
||||
self.followers = _.uniq(results.followers.concat(self.followers), 'id');
|
||||
if (results.subtypes) { //read_followers will return False if current user is not in the list
|
||||
@@ -241,27 +241,27 @@ var Followers = AbstractField.extend({
|
||||
*/
|
||||
_unfollow: function (ids) {
|
||||
var self = this;
|
||||
var def = $.Deferred();
|
||||
var text = _t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document.\n Do you really want to remove this follower ?");
|
||||
Dialog.confirm(this, text, {
|
||||
confirm_callback: function () {
|
||||
var args = [
|
||||
[self.res_id],
|
||||
ids.partner_ids,
|
||||
ids.channel_ids,
|
||||
{}, // FIXME
|
||||
];
|
||||
self._rpc({
|
||||
return new Promise(function (resolve, reject) {
|
||||
var text = _t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document.\n Do you really want to remove this follower ?");
|
||||
Dialog.confirm(this, text, {
|
||||
confirm_callback: function () {
|
||||
var args = [
|
||||
[self.res_id],
|
||||
ids.partner_ids,
|
||||
ids.channel_ids,
|
||||
{}, // FIXME
|
||||
];
|
||||
self._rpc({
|
||||
model: self.model,
|
||||
method: 'message_unsubscribe',
|
||||
args: args
|
||||
})
|
||||
.then(self._reload.bind(self));
|
||||
def.resolve();
|
||||
},
|
||||
cancel_callback: def.reject.bind(def),
|
||||
resolve();
|
||||
},
|
||||
cancel_callback: reject,
|
||||
});
|
||||
});
|
||||
return def;
|
||||
},
|
||||
_updateSubscription: function (event, followerID, isChannel) {
|
||||
var ids = {};
|
||||
@@ -290,7 +290,7 @@ var Followers = AbstractField.extend({
|
||||
|
||||
// If no more subtype followed, unsubscribe the follower
|
||||
if (!checklist.length) {
|
||||
this._unfollow(ids).fail(function () {
|
||||
this._unfollow(ids).guardedCatch(function () {
|
||||
$(event.currentTarget).find('input').addBack('input').prop('checked', true);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -50,18 +50,24 @@ FormRenderer.include({
|
||||
* @private
|
||||
*/
|
||||
_renderNode: function (node) {
|
||||
var self = this;
|
||||
if (node.tag === 'div' && node.attrs.class === 'oe_chatter') {
|
||||
if (!this.chatter) {
|
||||
this.chatter = new Chatter(this, this.state, this.mailFields, {
|
||||
isEditable: this.activeActions.edit,
|
||||
viewType: 'form',
|
||||
});
|
||||
this.chatter.appendTo($('<div>'));
|
||||
this._handleAttributes(this.chatter.$el, node);
|
||||
|
||||
var $temporaryParentDiv = $('<div>');
|
||||
this.defs.push(this.chatter.appendTo($temporaryParentDiv).then(function () {
|
||||
self.chatter.$el.unwrap();
|
||||
self._handleAttributes(self.chatter.$el, node);
|
||||
}));
|
||||
return $temporaryParentDiv;
|
||||
} else {
|
||||
this.chatter.update(this.state);
|
||||
return this.chatter.$el;
|
||||
}
|
||||
return this.chatter.$el;
|
||||
} else {
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ BasicModel.include({
|
||||
* @private
|
||||
* @param {Object} record - an element from the localData
|
||||
* @param {string} fieldName
|
||||
* @return {Deferred<Object>} the deferred is resolved with the
|
||||
* @return {Promise<Object>} the promise is resolved with the
|
||||
* invalidPartnerIds
|
||||
*/
|
||||
_setInvalidMany2ManyTagsEmail: function (record, fieldName) {
|
||||
@@ -42,10 +42,10 @@ BasicModel.include({
|
||||
var changes = {operation: 'DELETE', ids: _.pluck(invalidPartnerIds, 'id')};
|
||||
def = this._applyX2ManyChange(record, fieldName, changes);
|
||||
}
|
||||
return $.when(def).then(function () {
|
||||
return $.when({
|
||||
return Promise.resolve(def).then(function () {
|
||||
return {
|
||||
invalidPartnerIds: _.pluck(invalidPartnerIds, 'res_id'),
|
||||
});
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -65,7 +65,7 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
|
||||
* Open a popup for each invalid partners (without email) to fill the email.
|
||||
*
|
||||
* @private
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_checkEmailPopup: function () {
|
||||
var self = this;
|
||||
@@ -75,25 +75,25 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
|
||||
|
||||
// propose the user to correct invalid partners
|
||||
_.each(this.record.specialData[this.name].invalidPartnerIds, function (resID) {
|
||||
var def = $.Deferred();
|
||||
popupDefs.push(def);
|
||||
|
||||
var pop = new form_common.FormViewDialog(self, {
|
||||
res_model: self.field.relation,
|
||||
res_id: resID,
|
||||
context: self.record.context,
|
||||
title: "",
|
||||
on_saved: function (record) {
|
||||
if (record.data.email) {
|
||||
validPartners.push(record.res_id);
|
||||
}
|
||||
},
|
||||
}).open();
|
||||
pop.on('closed', self, function () {
|
||||
def.resolve();
|
||||
var def = new Promise(function (resolve, reject) {
|
||||
var pop = new form_common.FormViewDialog(self, {
|
||||
res_model: self.field.relation,
|
||||
res_id: resID,
|
||||
context: self.record.context,
|
||||
title: "",
|
||||
on_saved: function (record) {
|
||||
if (record.data.email) {
|
||||
validPartners.push(record.res_id);
|
||||
}
|
||||
},
|
||||
}).open();
|
||||
pop.on('closed', self, function () {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
popupDefs.push(def);
|
||||
});
|
||||
return $.when.apply($, popupDefs).then(function() {
|
||||
return Promise.all(popupDefs).then(function() {
|
||||
// All popups have been processed for the given ids
|
||||
// It is now time to set the final value with valid partners ids.
|
||||
validPartners = _.uniq(validPartners);
|
||||
@@ -117,14 +117,14 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
|
||||
*/
|
||||
_render: function () {
|
||||
var self = this;
|
||||
var def = $.Deferred();
|
||||
var _super = this._super.bind(this);
|
||||
if (this.record.specialData[this.name].invalidPartnerIds.length) {
|
||||
def = this._checkEmailPopup();
|
||||
} else {
|
||||
def.resolve();
|
||||
}
|
||||
return def.then(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (self.record.specialData[self.name].invalidPartnerIds.length) {
|
||||
resolve(self._checkEmailPopup());
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}).then(function () {
|
||||
return _super.apply(self, arguments);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -511,7 +511,7 @@ var Message = AbstractMessage.extend(Mixins.EventDispatcherMixin, ServicesMixin
|
||||
* @see {mail.Manager.Notification} for the receipt of 'toggle_star'
|
||||
* notification after this rpc.
|
||||
*
|
||||
* @return {$.Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
toggleStarStatus: function () {
|
||||
return this._rpc({
|
||||
|
||||
@@ -150,18 +150,18 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
* Mark the thread as read, which resets the unread counter to 0. This is
|
||||
* only performed if the unread counter is not 0.
|
||||
*
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
markAsRead: function () {
|
||||
if (this._unreadCounter > 0) {
|
||||
return this._markAsRead();
|
||||
}
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* Post a message on this thread
|
||||
*
|
||||
* @returns {$.Promise} resolved with the message object to be sent to the
|
||||
* @returns {Promise} resolved with the message object to be sent to the
|
||||
* server
|
||||
*/
|
||||
postMessage: function () {
|
||||
@@ -200,22 +200,22 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
* Mark the thread as read
|
||||
*
|
||||
* @private
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_markAsRead: function () {
|
||||
this.resetUnreadCounter();
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* Post a message on this thread
|
||||
*
|
||||
* @abstract
|
||||
* @private
|
||||
* @returns {$.Promise} resolved with the message object to be sent to the
|
||||
* @returns {Promise} resolved with the message object to be sent to the
|
||||
* server
|
||||
*/
|
||||
_postMessage: function () {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* Warn views (e.g. discuss app, thread window, etc.) to update visually
|
||||
|
||||
@@ -70,7 +70,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
this._isMyselfModerator = data.is_moderator;
|
||||
this._lastMessageDate = undefined;
|
||||
this._members = data.members || [];
|
||||
// Deferred that is resolved on fetched members of this channel.
|
||||
// Promise that is resolved on fetched members of this channel.
|
||||
this._membersDef = undefined;
|
||||
// number of messages that are 'needaction', which is equivalent to the
|
||||
// number of messages in this channel that are in inbox.
|
||||
@@ -129,19 +129,21 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
* @pverride
|
||||
*/
|
||||
detach: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._rpc({
|
||||
model: 'mail.channel',
|
||||
method: 'channel_minimize',
|
||||
args: [this.getUUID(), true],
|
||||
}, {
|
||||
shadow: true,
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
self._rpc({
|
||||
model: 'mail.channel',
|
||||
method: 'channel_minimize',
|
||||
args: [self.getUUID(), true],
|
||||
}, {
|
||||
shadow: true,
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Force fetch members of the channel
|
||||
*
|
||||
* @returns {$.Promise<Object[]>} resolved with list of channel listeners
|
||||
* @returns {Promise<Object[]>} resolved with list of channel listeners
|
||||
*/
|
||||
forceFetchMembers: function () {
|
||||
this._membersDef = undefined;
|
||||
@@ -186,7 +188,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
/**
|
||||
* Get listeners of a channel
|
||||
*
|
||||
* @returns {$.Promise<Array<Object[]>>} resolved with list of list of
|
||||
* @returns {Promise<Object[]>} resolved with list of list of
|
||||
* channel listeners.
|
||||
*/
|
||||
getMentionPartnerSuggestions: function () {
|
||||
@@ -302,7 +304,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
* Unsubscribes from channel
|
||||
*
|
||||
* @abstract
|
||||
* @returns {$.Promise} resolve when unsubscribed
|
||||
* @returns {Promise} resolve when unsubscribed
|
||||
*/
|
||||
unsubscribe: function () {},
|
||||
/**
|
||||
@@ -381,19 +383,19 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @returns {$.Promise} resolved when message has been marked as read
|
||||
* @returns {Promise} resolved when message has been marked as read
|
||||
*/
|
||||
_markAsRead: function () {
|
||||
var superDef = this._super.apply(this, arguments);
|
||||
var seenDef = this._notifySeen();
|
||||
return $.when(superDef, seenDef);
|
||||
return Promise.all([superDef, seenDef]);
|
||||
},
|
||||
/**
|
||||
* @override {mail.model.ThreadTypingMixin}
|
||||
* @private
|
||||
* @param {Object} params
|
||||
* @param {boolean} params.typing
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_notifyMyselfTyping: function (params) {
|
||||
return this._rpc({
|
||||
@@ -424,7 +426,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
* @override
|
||||
* @private
|
||||
* @param {Object} data data related to the new message
|
||||
* @returns {$.Promise<Object>} resolved when the message has been sent to
|
||||
* @returns {Promise<Object>} resolved when the message has been sent to
|
||||
* the server, with the object message that has been sent to the server.
|
||||
*/
|
||||
_postMessage: function (data) {
|
||||
|
||||
@@ -92,7 +92,7 @@ var DocumentThread = Thread.extend({
|
||||
* @param {Object} options
|
||||
* @param {boolean} [options.forceFetch] if true, fetch anyway, as user
|
||||
* clicked on 'load more'.
|
||||
* @returns {$.Promise<mail.model.Message[]>}
|
||||
* @returns {Promise<mail.model.Message[]>}
|
||||
*/
|
||||
fetchMessages: function (options) {
|
||||
var self = this;
|
||||
@@ -217,14 +217,14 @@ var DocumentThread = Thread.extend({
|
||||
* Get most up to date messageIDs
|
||||
*
|
||||
* @private
|
||||
* @returns {$.Promise} resolved when message IDs have been fetched and set
|
||||
* @returns {Promise} resolved when message IDs have been fetched and set
|
||||
* in the model
|
||||
*/
|
||||
_fetchMessageIDs: function () {
|
||||
var self = this;
|
||||
var resID = this.getDocumentID();
|
||||
if (!resID || !this._mustFetchMessageIDs) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._rpc({
|
||||
model: this.getDocumentModel(),
|
||||
@@ -239,7 +239,7 @@ var DocumentThread = Thread.extend({
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @param {boolean} [options.forceFetch]
|
||||
* @returns {$.Promise} resolved when messages have been fetched + document
|
||||
* @returns {Promise} resolved when messages have been fetched + document
|
||||
* thread has updated messages
|
||||
*/
|
||||
_fetchMessages: function (options) {
|
||||
@@ -273,7 +273,7 @@ var DocumentThread = Thread.extend({
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
});
|
||||
@@ -284,7 +284,7 @@ var DocumentThread = Thread.extend({
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @returns {$.Promise} resolved when messages have been marked as read on
|
||||
* @returns {Promise} resolved when messages have been marked as read on
|
||||
* the server.
|
||||
*/
|
||||
_markAsRead: function () {
|
||||
@@ -299,7 +299,7 @@ var DocumentThread = Thread.extend({
|
||||
* @override
|
||||
* @private
|
||||
* @param {Object} data data related to the new message
|
||||
* @return {$.Promise<Object>} resolved when the message has been sent to
|
||||
* @return {Promise<Object>} resolved when the message has been sent to
|
||||
* the server, with the object message that has been sent to the server.
|
||||
*/
|
||||
_postMessage: function (data) {
|
||||
|
||||
@@ -34,7 +34,7 @@ var Livechat = TwoUserChannel.extend({
|
||||
* display of a user that is typing.
|
||||
*
|
||||
* @override
|
||||
* @returns {$.Promise<Array<Object[]>>} resolved with list of list of
|
||||
* @returns {Promise<Object[]>} resolved with list of list of
|
||||
* livechat members.
|
||||
*/
|
||||
getMentionPartnerSuggestions: function () {
|
||||
|
||||
@@ -59,7 +59,7 @@ var Mailbox = SearchableThread.extend({
|
||||
* channel, only keep the lastest message of this document/channel in the
|
||||
* previews.
|
||||
*
|
||||
* @returns {$.Promise<Object[]>}
|
||||
* @returns {Promise<Object[]>}
|
||||
*/
|
||||
getMessagePreviews: function () {
|
||||
var self = this;
|
||||
@@ -119,7 +119,7 @@ var Mailbox = SearchableThread.extend({
|
||||
* makes only sense for 'Inbox'.
|
||||
*
|
||||
* @param {Array} domain
|
||||
* @return {$.Promise} resolved when all messages have been marked as read
|
||||
* @return {Promise} resolved when all messages have been marked as read
|
||||
* on the server
|
||||
*/
|
||||
markAllMessagesAsRead: function (domain) {
|
||||
@@ -133,7 +133,7 @@ var Mailbox = SearchableThread.extend({
|
||||
},
|
||||
});
|
||||
}
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -239,11 +239,11 @@ var ThreadTypingMixin = {
|
||||
* @private
|
||||
* @param {Object} params
|
||||
* @param {boolean} params.typing whether we are typing something or not
|
||||
* @returns {$.Promise} resolved if the server is notified, rejected
|
||||
* @returns {Promise} resolved if the server is notified, rejected
|
||||
* otherwise
|
||||
*/
|
||||
_notifyMyselfTyping: function (params) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* Warn views that the list of users that are currently typing on this
|
||||
|
||||
@@ -56,7 +56,7 @@ var MultiUserChannel = Channel.extend({
|
||||
* Unsubscribes from channel
|
||||
*
|
||||
* @override
|
||||
* @returns {$.Promise} resolve when unsubscribed
|
||||
* @returns {Promise} resolve when unsubscribed
|
||||
*/
|
||||
unsubscribe: function () {
|
||||
return this._rpc({
|
||||
|
||||
@@ -49,7 +49,7 @@ var SearchableThread = Thread.extend({
|
||||
}
|
||||
var cache = this._getCache(domain);
|
||||
if (cache.loaded) {
|
||||
return $.when(cache.messages);
|
||||
return Promise.all(cache.messages);
|
||||
} else {
|
||||
return this._fetchMessages(domain);
|
||||
}
|
||||
@@ -159,7 +159,7 @@ var SearchableThread = Thread.extend({
|
||||
* @private
|
||||
* @param {Array} [pDomain] filter on the messages of the channel
|
||||
* @param {boolean} [loadMore] Whether it should load more message
|
||||
* @return {$.Promise<mail.model.Message[]>} resolved with list of messages
|
||||
* @return {Promise<mail.model.Message[]>} resolved with list of messages
|
||||
*/
|
||||
_fetchMessages: function (pDomain, loadMore) {
|
||||
var self = this;
|
||||
|
||||
@@ -58,12 +58,13 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
* of this thread. Otherwise unfold it while detaching it.
|
||||
* @param {boolean} [options.passively=false] if set, if the thread window
|
||||
* will be created passively.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
detach: function (options) {
|
||||
options = options || {};
|
||||
this._detached = true;
|
||||
this._folded = options.keepFoldState ? this._folded : false;
|
||||
this._warnUpdatedWindowState({
|
||||
return this._warnUpdatedWindowState({
|
||||
passively: options.passively,
|
||||
});
|
||||
},
|
||||
@@ -79,10 +80,10 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
* `this.LIMIT` number of messages at a time.
|
||||
*
|
||||
* @abstract
|
||||
* @returns {$.Promise<mail.model.Message[]>}
|
||||
* @returns {Promise<mail.model.Message[]>}
|
||||
*/
|
||||
fetchMessages: function () {
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
/**
|
||||
* Updates the folded state of the thread. Must be overriden to reflect
|
||||
@@ -110,10 +111,10 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
* By default, a thread has not listener.
|
||||
*
|
||||
* @abstract
|
||||
* @returns {$.Promise<Array<Object[]>>}
|
||||
* @returns {Promise<Array<Object[]>>}
|
||||
*/
|
||||
getMentionPartnerSuggestions: function () {
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
/**
|
||||
* Returns the information required to render the preview of this channel.
|
||||
@@ -287,10 +288,10 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
/**
|
||||
* @abstract
|
||||
* @private
|
||||
* @return {$.Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_fetchMessages: function () {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* Replace character representations of emojis by their unicode
|
||||
@@ -330,7 +331,7 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
* @abstract
|
||||
* @private
|
||||
* @param {Object} data
|
||||
* @returns {$.Promise<Object>} resolved with the message object to be sent
|
||||
* @returns {Promise<Object>} resolved with the message object to be sent
|
||||
* to the server
|
||||
*/
|
||||
_postMessage: function (data) {
|
||||
@@ -355,7 +356,7 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
messageData.subject = data.subject;
|
||||
}
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
return $.when(messageData);
|
||||
return messageData;
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -374,10 +375,11 @@ var Thread = AbstractThread.extend(ServicesMixin, {
|
||||
* @private
|
||||
* @param {Object} [options={}]
|
||||
* @param {boolean} [options.passively=false]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_warnUpdatedWindowState: function (options) {
|
||||
options = options || {};
|
||||
this.call('mail_service', 'updateThreadWindow', this.getID(), options);
|
||||
return this.call('mail_service', 'updateThreadWindow', this.getID(), options);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ var TwoUserChannel = Channel.extend({
|
||||
* Unpin from two-user thread
|
||||
*
|
||||
* @override
|
||||
* @returns {$.Promise} resolve when unsubscribed
|
||||
* @returns {Promise} resolve when unsubscribed
|
||||
*/
|
||||
unsubscribe: function () {
|
||||
return this._rpc({
|
||||
|
||||
@@ -182,7 +182,7 @@ MailManager.include({
|
||||
resModel: resModel,
|
||||
});
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* Updates the thread windows related to document threads.
|
||||
|
||||
@@ -80,13 +80,14 @@ var MailManager = AbstractService.extend({
|
||||
* the unread_counter of channel.
|
||||
* @param {boolean} [options.silent] whether it should inform in the mailBus
|
||||
* of the newly created message.
|
||||
* @returns {mail.model.Message} message object
|
||||
* @returns {Promise<mail.model.Message>}
|
||||
*/
|
||||
addMessage: function (data, options) {
|
||||
options = options || {};
|
||||
var message = this.getMessage(data.id);
|
||||
var prom = Promise.resolve();
|
||||
if (!message) {
|
||||
message = this._addNewMessage(data, options);
|
||||
prom = this._addNewMessage(data, options);
|
||||
} else {
|
||||
if (data.moderation_status === 'accepted') {
|
||||
message.setModerationStatus('accepted', {
|
||||
@@ -97,7 +98,7 @@ var MailManager = AbstractService.extend({
|
||||
this._addMessageToThreads(message, options);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
return prom;
|
||||
},
|
||||
/**
|
||||
* Creates a channel, can be either a true channel or a DM chat based on
|
||||
@@ -105,7 +106,7 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @param {integer|string} name id of partner (in case of dm) or name
|
||||
* @param {string} type ['dm_chat', 'public', 'private']
|
||||
* @returns {$.Promise<integer>} resolved with ID of the newly created
|
||||
* @returns {Promise<integer>} resolved with ID of the newly created
|
||||
* channel
|
||||
*/
|
||||
createChannel: function (name, type) {
|
||||
@@ -150,7 +151,7 @@ var MailManager = AbstractService.extend({
|
||||
* Returns the content that will be shown in the mail navbar dropdown
|
||||
*
|
||||
* @param {mail.model.Channel[]} channels
|
||||
* @returns {$.Promise<Object[]>} resolved with list of channel previews
|
||||
* @returns {Promise<Object[]>} resolved with list of channel previews
|
||||
*/
|
||||
getChannelPreviews: function (channels) {
|
||||
var self = this;
|
||||
@@ -227,23 +228,22 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @param {string} [filter=undefined] specify 'chat' or 'channels' to only
|
||||
* get previews for that type of threads
|
||||
* @returns {$.Promise<Object[]>} resolved with list of objects that have a
|
||||
* @returns {Promise<Object[]>} resolved with list of objects that have a
|
||||
* valid format for rendering a messaging menu preview items.
|
||||
*/
|
||||
getSystrayPreviews: function (filter) {
|
||||
var self = this;
|
||||
var defs = [];
|
||||
|
||||
var channelDef = this._getSystrayChannelPreviews(filter);
|
||||
var inboxDef = this._getSystrayInboxPreviews(filter);
|
||||
var failureDef = this._getSystrayMailFailurePreviews(filter);
|
||||
defs = defs.concat([channelDef, inboxDef, failureDef]);
|
||||
return $.when.apply($, defs)
|
||||
.then(function (previewsChannel, previewsInbox, previewsFailure) {
|
||||
|
||||
return Promise.all([channelDef, inboxDef, failureDef])
|
||||
.then(function (result) { //previewsChannel, previewsInbox, previewsFailure
|
||||
// order: failures > inbox > channel, each group must be sorted
|
||||
previewsChannel = self._sortPreviews(previewsChannel);
|
||||
previewsInbox = self._sortPreviews(previewsInbox);
|
||||
previewsFailure = self._sortPreviews(previewsFailure);
|
||||
var previewsChannel = self._sortPreviews(result[0]);
|
||||
var previewsInbox = self._sortPreviews(result[1]);
|
||||
var previewsFailure = self._sortPreviews(result[2]);
|
||||
return _.union(previewsFailure, previewsInbox, previewsChannel);
|
||||
});
|
||||
},
|
||||
@@ -295,7 +295,7 @@ var MailManager = AbstractService.extend({
|
||||
* This is the case when it has fetched the initial state from the server,
|
||||
* by means of the route 'mail/init_messaging'
|
||||
*
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
isReady: function () {
|
||||
return this._isReady;
|
||||
@@ -306,24 +306,24 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @param {integer} channelID
|
||||
* @param {Object} [options] options to be passed on channel add
|
||||
* @returns {$.Promise<integer>} resolved with channelID joined
|
||||
* @returns {Promise<integer>} resolved with channelID joined
|
||||
*/
|
||||
joinChannel: function (channelID, options) {
|
||||
var def;
|
||||
var prom;
|
||||
var channel = this.getChannel(channelID);
|
||||
if (channel) {
|
||||
// channel already joined
|
||||
def = $.when(channelID);
|
||||
prom = Promise.resolve(channelID);
|
||||
} else {
|
||||
def = this._joinAndAddChannel(channelID, options);
|
||||
prom = this._joinAndAddChannel(channelID, options);
|
||||
}
|
||||
return def;
|
||||
return prom;
|
||||
},
|
||||
/**
|
||||
* Mark messages as read
|
||||
*
|
||||
* @param {Array} messageIDs list of messages IDs
|
||||
* @returns {$.Promise} resolved when messages have been marked as read on
|
||||
* @returns {Promise} resolved when messages have been marked as read on
|
||||
* the server.
|
||||
*/
|
||||
markMessagesAsRead: function (messageIDs) {
|
||||
@@ -341,7 +341,7 @@ var MailManager = AbstractService.extend({
|
||||
args: [ids],
|
||||
});
|
||||
} else {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -396,19 +396,19 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @param {string} searchVal
|
||||
* @param {integer} limit max number of found partners in the response
|
||||
* @returns {$.Promise<Object[]>} list of found partners (matching
|
||||
* @returns {Promise<Object[]>} list of found partners (matching
|
||||
* 'searchVal')
|
||||
*/
|
||||
searchPartner: function (searchVal, limit) {
|
||||
var def = $.Deferred();
|
||||
var self = this;
|
||||
var partners = this._searchPartnerPrefetch(searchVal, limit);
|
||||
|
||||
if (!partners.length) {
|
||||
def = this._searchPartnerFetch(searchVal, limit);
|
||||
} else {
|
||||
def = $.when(partners);
|
||||
}
|
||||
return def.then(function (partners) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!partners.length) {
|
||||
resolve(self._searchPartnerFetch(searchVal, limit));
|
||||
} else {
|
||||
resolve(partners);
|
||||
}
|
||||
}).then(function (partners) {
|
||||
var suggestions = _.map(partners, function (partner) {
|
||||
return {
|
||||
id: partner.id,
|
||||
@@ -422,7 +422,7 @@ var MailManager = AbstractService.extend({
|
||||
/**
|
||||
* Unstars all messages from all channels
|
||||
*
|
||||
* @returns {$.Promise}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
unstarAll: function () {
|
||||
return this._rpc({
|
||||
@@ -448,11 +448,13 @@ var MailManager = AbstractService.extend({
|
||||
* @param {string} [data.state] e.g. 'open', 'folded'
|
||||
* @param {Object|integer} [options=undefined]
|
||||
* @param {boolean} [options.silent=false]
|
||||
* @returns {integer} the ID of the newly or already existing channel
|
||||
* @returns {Promise<integer>} resolves with the ID of the newly or already
|
||||
* existing channel
|
||||
*/
|
||||
_addChannel: function (data, options) {
|
||||
options = typeof options === 'object' ? options : {};
|
||||
var channel = this.getChannel(data.id);
|
||||
var proms = [];
|
||||
if (!channel) {
|
||||
channel = this._makeChannel(data, options);
|
||||
if (channel.getType() === 'dm_chat') {
|
||||
@@ -462,14 +464,16 @@ var MailManager = AbstractService.extend({
|
||||
if (data.last_message) {
|
||||
// channel_info in mobile, necessary for showing channel
|
||||
// preview in mobile
|
||||
this.addMessage(data.last_message);
|
||||
proms.push(this.addMessage(data.last_message));
|
||||
}
|
||||
this._sortThreads();
|
||||
if (!options.silent) {
|
||||
this._mailBus.trigger('new_channel', channel);
|
||||
this._mailBus.trigger('new_channel', channel, proms);
|
||||
}
|
||||
}
|
||||
return channel.getID();
|
||||
return Promise.all(proms).then(function () {
|
||||
return channel.getID();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Add a new mailbox
|
||||
@@ -516,21 +520,23 @@ var MailManager = AbstractService.extend({
|
||||
* @private
|
||||
* @param {Object} data
|
||||
* @param {Object} options
|
||||
* @returns {mail.model.Message}
|
||||
* @return {Promise<mail.model.Message>}
|
||||
*/
|
||||
_addNewMessage: function (data, options) {
|
||||
var self = this;
|
||||
var message = this._makeMessage(data);
|
||||
// Keep the array ordered by ID when inserting the new message
|
||||
var index = _.sortedIndex(this._messages, message, function (msg) {
|
||||
return msg.getID();
|
||||
});
|
||||
this._messages.splice(index, 0, message);
|
||||
this._addNewMessagePostprocessThread(message, options);
|
||||
this._addMessageToThreads(message, options);
|
||||
if (!options.silent) {
|
||||
this._mailBus.trigger('new_message', message);
|
||||
}
|
||||
return message;
|
||||
return this._addNewMessagePostprocessThread(message, options).then(function () {
|
||||
self._addMessageToThreads(message, options);
|
||||
if (!options.silent) {
|
||||
self._mailBus.trigger('new_message', message);
|
||||
}
|
||||
return message;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* For newly added message, postprocess threads linked to this message
|
||||
@@ -541,9 +547,11 @@ var MailManager = AbstractService.extend({
|
||||
* @param {Array} [options.domain]
|
||||
* @param {boolean} [options.incrementUnread]
|
||||
* @param {boolean} [options.showNotification]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_addNewMessagePostprocessThread: function (message, options) {
|
||||
var self = this;
|
||||
var proms = [];
|
||||
_.each(message.getThreadIDs(), function (threadID) {
|
||||
var thread = self.getThread(threadID);
|
||||
if (thread) {
|
||||
@@ -553,6 +561,7 @@ var MailManager = AbstractService.extend({
|
||||
!message.isSystemNotification()
|
||||
) {
|
||||
if (thread.isTwoUserThread() && options.showNotification) {
|
||||
var prom = Promise.resolve();
|
||||
if (
|
||||
!self._isDiscussOpen() &&
|
||||
!config.device.isMobile &&
|
||||
@@ -560,17 +569,21 @@ var MailManager = AbstractService.extend({
|
||||
) {
|
||||
// automatically open thread window
|
||||
// while keeping it unread
|
||||
thread.detach({ passively: true });
|
||||
}
|
||||
var query = { isVisible: false };
|
||||
self._mailBus.trigger('is_thread_bottom_visible', thread, query);
|
||||
if (!self.call('bus_service', 'isOdooFocused') || !query.isVisible) {
|
||||
self._notifyIncomingMessage(message);
|
||||
prom = thread.detach({ passively: true });
|
||||
}
|
||||
proms.push(prom);
|
||||
prom.then(function () {
|
||||
var query = { isVisible: false };
|
||||
self._mailBus.trigger('is_thread_bottom_visible', thread, query);
|
||||
if (!self.call('bus_service', 'isOdooFocused') || !query.isVisible) {
|
||||
self._notifyIncomingMessage(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return Promise.all(proms);
|
||||
},
|
||||
/**
|
||||
* Create a Channel (other than a DM)
|
||||
@@ -578,7 +591,7 @@ var MailManager = AbstractService.extend({
|
||||
* @private
|
||||
* @param {string} name
|
||||
* @param {string} type
|
||||
* @returns {$.Promise<integer>} ID of the created channel
|
||||
* @returns {Promise<integer>} ID of the created channel
|
||||
*/
|
||||
_createChannel: function (name, type) {
|
||||
var context = _.extend({ isMobile: config.device.isMobile }, session.user_context);
|
||||
@@ -595,7 +608,7 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {string} name
|
||||
* @returns {$.Promise<integer>} ID of the created channel
|
||||
* @returns {Promise<integer>} ID of the created channel
|
||||
*/
|
||||
_createDMChat: function (name) {
|
||||
var context = _.extend({ isMobile: config.device.isMobile }, session.user_context);
|
||||
@@ -610,7 +623,7 @@ var MailManager = AbstractService.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {integer[]} channelIDs
|
||||
* @returns {$.Promise<Object[]>} resolved with list of channel preview
|
||||
* @returns {Promise<Object[]>} resolved with list of channel preview
|
||||
*/
|
||||
_fetchChannelPreviews: function (channelIDs) {
|
||||
return this._rpc({
|
||||
@@ -652,7 +665,7 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {mail.model.Channel[]} channels
|
||||
* @returns {$.Deferred<Object[]>} resolved with list of channel previews
|
||||
* @returns {Promise<Object[]>} resolved with list of channel previews
|
||||
*/
|
||||
_getChannelPreviews: function (channels) {
|
||||
var self = this;
|
||||
@@ -667,19 +680,22 @@ var MailManager = AbstractService.extend({
|
||||
});
|
||||
fetchDef = this._fetchChannelPreviews(ids);
|
||||
} else {
|
||||
fetchDef = $.when();
|
||||
fetchDef = Promise.resolve();
|
||||
}
|
||||
return fetchDef.then(function (fetchedPreviews) {
|
||||
// add fetched messages
|
||||
var proms = [];
|
||||
_.each(fetchedPreviews, function (fetchedPreview) {
|
||||
if (fetchedPreview.last_message) {
|
||||
self.addMessage(fetchedPreview.last_message);
|
||||
proms.push(self.addMessage(fetchedPreview.last_message));
|
||||
}
|
||||
// mark the channel as previewed, so that we do not need to
|
||||
// fetch preview again
|
||||
var channel = self.getChannel(fetchedPreview.id);
|
||||
channel.markAsPreviewed();
|
||||
});
|
||||
return Promise.all(proms);
|
||||
}).then(function () {
|
||||
return _.map(channels, function (channel) {
|
||||
return channel.getPreview();
|
||||
});
|
||||
@@ -694,7 +710,7 @@ var MailManager = AbstractService.extend({
|
||||
* the documentID is omitted for a model preview, whereas it is set for a
|
||||
* preview of a failure related to a document.
|
||||
*
|
||||
* @returns {$.Promise<Object[]>} resolved with previews of mail failures
|
||||
* @returns {Promise<Object[]>} resolved with previews of mail failures
|
||||
*/
|
||||
_getMailFailurePreviews: function () {
|
||||
// items = list of objects:
|
||||
@@ -754,7 +770,7 @@ var MailManager = AbstractService.extend({
|
||||
* @private
|
||||
* @param {string|undefined} [filter] the filter to apply on channel
|
||||
* previews in systray messaging menu
|
||||
* @returns {$.Promise<Object[]>} resolved with object valid for template
|
||||
* @returns {Promise<Object[]>} resolved with object valid for template
|
||||
* mail.Preview on channel previews, based on selected filter
|
||||
*/
|
||||
_getSystrayChannelPreviews: function (filter) {
|
||||
@@ -796,7 +812,7 @@ var MailManager = AbstractService.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {string|undefined} [filter]
|
||||
* @returns {$.Deferred<Object[]>} resolved with valid objects for template
|
||||
* @returns {Promise<Object[]>} resolved with valid objects for template
|
||||
* mail.Preview on inbox message previews
|
||||
*/
|
||||
_getSystrayInboxPreviews: function (filter) {
|
||||
@@ -806,7 +822,7 @@ var MailManager = AbstractService.extend({
|
||||
var inbox = this.getMailbox('inbox');
|
||||
return inbox.getMessagePreviews();
|
||||
} else {
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -820,7 +836,7 @@ var MailManager = AbstractService.extend({
|
||||
if (filter === 'mailbox_inbox' || !filter) {
|
||||
return this._getMailFailurePreviews();
|
||||
} else {
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -863,7 +879,7 @@ var MailManager = AbstractService.extend({
|
||||
* @private
|
||||
* @param {integer|string} channelID
|
||||
* @param {Object} options options passed on channel add
|
||||
* @returns {$.Promise<integer>} channelID of joined and added channel
|
||||
* @returns {Promise<integer>} channelID of joined and added channel
|
||||
*/
|
||||
_joinAndAddChannel: function (channelID, options) {
|
||||
var self = this;
|
||||
@@ -941,7 +957,7 @@ var MailManager = AbstractService.extend({
|
||||
this._toNotifyChannelFetchedIDs = [];
|
||||
if (_.isEmpty(channelIDs)) {
|
||||
// no channel to notify fetched
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._rpc({
|
||||
model: 'mail.channel',
|
||||
@@ -1098,7 +1114,7 @@ var MailManager = AbstractService.extend({
|
||||
* @private
|
||||
* @param {string} searchVal
|
||||
* @param {integer} limit
|
||||
* @returns {$.Promise<Object[]>} fetched partners matching 'searchVal'
|
||||
* @returns {Promise<Object[]>} fetched partners matching 'searchVal'
|
||||
*/
|
||||
_searchPartnerFetch: function (searchVal, limit) {
|
||||
return this._rpc({
|
||||
@@ -1194,12 +1210,17 @@ var MailManager = AbstractService.extend({
|
||||
* (e.g. list of public channel data are stored in 'channel_channel')
|
||||
* @param {Object[]} [data.channel_slots[i] list of data of channel of type
|
||||
* `i`
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_updateChannelsFromServer: function (data) {
|
||||
var self = this;
|
||||
var proms = [];
|
||||
_.each(data.channel_slots, function (channels) {
|
||||
_.each(channels, self._addChannel.bind(self));
|
||||
_.each(channels, function (channel) {
|
||||
proms.push(self._addChannel(channel));
|
||||
});
|
||||
});
|
||||
return Promise.all(proms);
|
||||
},
|
||||
/**
|
||||
* Update commands from mail data fetched from the server
|
||||
@@ -1221,11 +1242,12 @@ var MailManager = AbstractService.extend({
|
||||
* @param {Array<Object[]>} result.mention_partner_suggestions list of
|
||||
* suggestions.
|
||||
* @param {integer} result.menu_id the menu ID of discuss app
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_updateInternalStateFromServer: function (result) {
|
||||
// commands are needed for channel instantiation
|
||||
this._updateCommandsFromServer(result);
|
||||
this._updateChannelsFromServer(result);
|
||||
var prom = this._updateChannelsFromServer(result);
|
||||
this._updateModerationSettingsFromServer(result);
|
||||
this._updateMailboxesFromServer(result);
|
||||
this._updateMailFailuresFromServer(result);
|
||||
@@ -1233,6 +1255,8 @@ var MailManager = AbstractService.extend({
|
||||
|
||||
this._mentionPartnerSuggestions = result.mention_partner_suggestions;
|
||||
this._discussMenuID = result.menu_id;
|
||||
|
||||
return prom;
|
||||
},
|
||||
/**
|
||||
* Update the mailboxes with mail data fetched from server, namely 'Inbox',
|
||||
|
||||
@@ -101,12 +101,12 @@ MailManager.include({
|
||||
channelAlreadyInCache = !!this.getChannel(messageData.channel_ids[0]);
|
||||
def = this.joinChannel(messageData.channel_ids[0], { autoswitch: false });
|
||||
} else {
|
||||
def = $.when();
|
||||
def = Promise.resolve();
|
||||
}
|
||||
def.then(function () {
|
||||
// don't increment unread if channel wasn't in cache yet as
|
||||
// its unread counter has just been fetched
|
||||
self.addMessage(messageData, {
|
||||
return self.addMessage(messageData, {
|
||||
showNotification: true,
|
||||
incrementUnread: channelAlreadyInCache
|
||||
});
|
||||
@@ -179,18 +179,19 @@ MailManager.include({
|
||||
_handleNeedactionNotification: function (messageData) {
|
||||
var self = this;
|
||||
var inbox = this.getMailbox('inbox');
|
||||
var message = this.addMessage(messageData, {
|
||||
this.addMessage(messageData, {
|
||||
incrementUnread: true,
|
||||
showNotification: true,
|
||||
}).then(function (message) {
|
||||
inbox.incrementMailboxCounter();
|
||||
_.each(message.getThreadIDs(), function (threadID) {
|
||||
var channel = self.getChannel(threadID);
|
||||
if (channel) {
|
||||
channel.incrementNeedactionCounter();
|
||||
}
|
||||
});
|
||||
self._mailBus.trigger('update_needaction', inbox.getMailboxCounter());
|
||||
});
|
||||
inbox.incrementMailboxCounter();
|
||||
_.each(message.getThreadIDs(), function (threadID) {
|
||||
var channel = self.getChannel(threadID);
|
||||
if (channel) {
|
||||
channel.incrementNeedactionCounter();
|
||||
}
|
||||
});
|
||||
this._mailBus.trigger('update_needaction', inbox.getMailboxCounter());
|
||||
},
|
||||
/**
|
||||
* Called when an activity record has been updated on the server
|
||||
@@ -381,8 +382,10 @@ MailManager.include({
|
||||
* @param {Object} data.message data of message
|
||||
*/
|
||||
_handlePartnerMessageModeratorNotification: function (data) {
|
||||
this.addMessage(data.message);
|
||||
this._mailBus.trigger('update_moderation_counter');
|
||||
var self = this;
|
||||
this.addMessage(data.message).then(function () {
|
||||
self._mailBus.trigger('update_moderation_counter');
|
||||
});
|
||||
},
|
||||
/**
|
||||
* On receiving a notification that is specific to a user
|
||||
|
||||
@@ -81,7 +81,7 @@ MailManager.include({
|
||||
var self = this;
|
||||
var partnerIDs = data.partnerIDs;
|
||||
if (_.isEmpty(partnerIDs)) {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._rpc({
|
||||
model: 'res.partner',
|
||||
|
||||
@@ -94,6 +94,7 @@ MailManager.include({
|
||||
* is not open yet, and do nothing otherwise.
|
||||
* @param {boolean} [options.keepFoldState=false] if set to true, keep the
|
||||
* fold state of the thread
|
||||
* @returns {Promise}
|
||||
*/
|
||||
openThreadWindow: function (threadID, options) {
|
||||
var self = this;
|
||||
@@ -108,10 +109,10 @@ MailManager.include({
|
||||
return;
|
||||
}
|
||||
var threadWindow = this._getThreadWindow(threadID);
|
||||
var def = $.when();
|
||||
var prom = Promise.resolve();
|
||||
if (!threadWindow) {
|
||||
thread.isCreatingWindow = true;
|
||||
def = thread.fetchMessages().then(function () {
|
||||
prom = thread.fetchMessages().then(function () {
|
||||
threadWindow = self._makeNewThreadWindow(thread, options);
|
||||
self._placeNewThreadWindow(threadWindow, options.passively);
|
||||
return threadWindow.appendTo($(self.THREAD_WINDOW_APPENDTO));
|
||||
@@ -126,12 +127,12 @@ MailManager.include({
|
||||
) {
|
||||
thread.markAsRead();
|
||||
}
|
||||
}).fail(function () {
|
||||
thread.isCreatingWindow = false;
|
||||
}).guardedCatch(function () {
|
||||
// thread window could not be open, which may happen due to
|
||||
// access error while fetching messages to the document.
|
||||
// abort opening the thread window in this case.
|
||||
thread.close();
|
||||
}).always(function () {
|
||||
thread.isCreatingWindow = false;
|
||||
});
|
||||
} else if (!options.passively) {
|
||||
@@ -139,7 +140,7 @@ MailManager.include({
|
||||
this._makeThreadWindowVisible(threadWindow);
|
||||
}
|
||||
}
|
||||
def.then(function () {
|
||||
return prom.then(function () {
|
||||
threadWindow.updateVisualFoldState();
|
||||
});
|
||||
},
|
||||
@@ -155,14 +156,16 @@ MailManager.include({
|
||||
*/
|
||||
updateThreadWindow: function (threadID, options) {
|
||||
var thread = this.getThread(threadID);
|
||||
var prom = Promise.resolve();
|
||||
if (thread) {
|
||||
if (thread.isDetached()) {
|
||||
_.extend(options, { keepFoldState: true });
|
||||
this.openThreadWindow(threadID, options);
|
||||
prom = this.openThreadWindow(threadID, options);
|
||||
} else {
|
||||
this._closeThreadWindow(threadID);
|
||||
}
|
||||
}
|
||||
return prom;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -429,7 +432,7 @@ MailManager.include({
|
||||
*
|
||||
* @private
|
||||
* @param {integer} partnerID
|
||||
* @returns {$.Promise<integer>} resolved with ID of the DM chat
|
||||
* @returns {Promise<integer>} resolved with ID of the DM chat
|
||||
*/
|
||||
_openAndDetachDMChat: function (partnerID) {
|
||||
return this._rpc({
|
||||
@@ -647,10 +650,16 @@ MailManager.include({
|
||||
*
|
||||
* @private
|
||||
* @param {mail.model.Channel} channel
|
||||
* @param {Promise[]} proms used to synchronize async operations on the
|
||||
* channel (like the rendering of its window)
|
||||
*/
|
||||
_onNewChannel: function (channel) {
|
||||
_onNewChannel: function (channel, proms) {
|
||||
if (channel.isDetached()) {
|
||||
this.openThreadWindow(channel.getID(), { keepFoldState: true, passively: true });
|
||||
var prom = this.openThreadWindow(channel.getID(), {
|
||||
keepFoldState: true,
|
||||
passively: true,
|
||||
});
|
||||
proms.push(prom);
|
||||
} else {
|
||||
this._closeThreadWindow(channel.getID());
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ var ActivityMenu = Widget.extend({
|
||||
'show.bs.dropdown': '_onActivityMenuShow',
|
||||
},
|
||||
willStart: function () {
|
||||
return $.when(this.call('mail_service', 'isReady'));
|
||||
return Promise.resolve(this.call('mail_service', 'isReady'));
|
||||
},
|
||||
start: function () {
|
||||
this._$activitiesPreview = this.$('.o_mail_systray_dropdown_items');
|
||||
|
||||
@@ -30,7 +30,7 @@ var MessagingMenu = Widget.extend({
|
||||
* @override
|
||||
*/
|
||||
willStart: function () {
|
||||
return $.when(this._super.apply(this, arguments), this.call('mail_service', 'isReady'));
|
||||
return Promise.all([this._super.apply(this, arguments), this.call('mail_service', 'isReady')]);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -112,7 +112,7 @@ var MessagingMenu = Widget.extend({
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {$.Promise<Object[]>} resolved with list of previews that are
|
||||
* @returns {Promise<Object[]>} resolved with list of previews that are
|
||||
* compatible with the 'mail.Preview' template.
|
||||
*/
|
||||
_getPreviews: function () {
|
||||
|
||||
@@ -57,7 +57,7 @@ var ThreadField = AbstractField.extend({
|
||||
var def1 = this._threadWidget.appendTo(this.$el);
|
||||
var def2 = this._super.apply(this, arguments);
|
||||
|
||||
return this.alive($.when(def1, def2)).then(function () {
|
||||
return this.alive(Promise.all([def1, def2])).then(function () {
|
||||
// unwrap the thread to remove an unnecessary level on div
|
||||
self.setElement(self._threadWidget.$el);
|
||||
var mailBus = self.call('mail_service', 'getMailBus');
|
||||
@@ -86,7 +86,7 @@ var ThreadField = AbstractField.extend({
|
||||
/**
|
||||
* @param {Object} message
|
||||
* @param {integer[]} message.partner_ids
|
||||
* @return {$.Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
postMessage: function (message) {
|
||||
var self = this;
|
||||
@@ -95,9 +95,9 @@ var ThreadField = AbstractField.extend({
|
||||
if (message.partner_ids.length) {
|
||||
self.trigger_up('reload_mail_fields', { followers: true });
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
}).guardedCatch(function () {
|
||||
self.do_notify(_t("Sending Error"), _t("Your message has not been sent."));
|
||||
return Promise.reject();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -109,7 +109,7 @@ var ThreadField = AbstractField.extend({
|
||||
* @private
|
||||
* @param {Object} [options]
|
||||
* @param {boolean} [options.forceFetch]
|
||||
* @return {$.Deferred}
|
||||
* @return {Promise}
|
||||
*/
|
||||
_fetchAndRenderThread: function (options) {
|
||||
var self = this;
|
||||
@@ -117,7 +117,7 @@ var ThreadField = AbstractField.extend({
|
||||
var thread = new CreateModeDocumentThread();
|
||||
options = { isCreateMode: true };
|
||||
self._threadWidget.render(thread, options);
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
var fetchDef = this.dp.add(this._documentThread.fetchMessages(options));
|
||||
return fetchDef.then(function () {
|
||||
@@ -134,7 +134,7 @@ var ThreadField = AbstractField.extend({
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
* @returns {$.Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_render: function () {
|
||||
return this._fetchAndRenderThread();
|
||||
|
||||
@@ -248,16 +248,16 @@ var ThreadWidget = Widget.extend({
|
||||
*/
|
||||
removeMessageAndRender: function (messageID, thread, options) {
|
||||
var self = this;
|
||||
var done = $.Deferred();
|
||||
this.$('.o_thread_message[data-message-id="' + messageID + '"]')
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.$('.o_thread_message[data-message-id="' + messageID + '"]')
|
||||
.fadeOut({
|
||||
done: function () {
|
||||
self.render(thread, options);
|
||||
done.resolve();
|
||||
resolve();
|
||||
},
|
||||
duration: 200,
|
||||
});
|
||||
return done;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Scroll to the bottom of the thread
|
||||
@@ -273,7 +273,7 @@ var ThreadWidget = Widget.extend({
|
||||
* @param {boolean} [options.onlyIfNecessary]
|
||||
*/
|
||||
scrollToMessage: function (options) {
|
||||
var $target = this.$('.o_thread_message[data-message-id="' + options.msgID + '"]');
|
||||
var $target = this.$('.o_thread_message[data-message-id="' + options.messageID + '"]');
|
||||
if (options.onlyIfNecessary) {
|
||||
var delta = $target.parent().height() - $target.height();
|
||||
var offset = delta < 0 ?
|
||||
|
||||
@@ -83,6 +83,8 @@ var AbstractThreadWindow = Widget.extend({
|
||||
displayStars: this.options.displayStars,
|
||||
});
|
||||
|
||||
// animate the (un)folding of thread windows
|
||||
this.$el.css({transition: 'height ' + this.FOLD_ANIMATION_DURATION + 'ms linear'});
|
||||
if (this.isFolded()) {
|
||||
this.$el.css('height', this.HEIGHT_FOLDED);
|
||||
} else if (this.options.autofocus) {
|
||||
@@ -95,7 +97,7 @@ var AbstractThreadWindow = Widget.extend({
|
||||
var def = this._threadWidget.replace(this.$('.o_thread_window_content')).then(function () {
|
||||
self._threadWidget.$el.on('scroll', self, self._debouncedOnScroll);
|
||||
});
|
||||
return $.when(this._super(), def);
|
||||
return Promise.all([this._super(), def]);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -319,24 +321,14 @@ var AbstractThreadWindow = Widget.extend({
|
||||
this._focusInput();
|
||||
}
|
||||
}
|
||||
this._animateFold();
|
||||
var height = this.isFolded() ? this.HEIGHT_FOLDED : this.HEIGHT_OPEN;
|
||||
this.$el.css({height: height});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called when there is a change of the fold state of the thread window.
|
||||
* This method animates the change of fold state of this thread window.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_animateFold: function () {
|
||||
this.$el.animate({
|
||||
height: this.isFolded() ? this.HEIGHT_FOLDED : this.HEIGHT_OPEN
|
||||
}, this.FOLD_ANIMATION_DURATION);
|
||||
},
|
||||
/**
|
||||
* Set the focus on the composer of the thread window. This operation is
|
||||
* ignored in mobile context.
|
||||
|
||||
@@ -83,7 +83,7 @@ var ThreadWindow = AbstractThreadWindow.extend({
|
||||
}
|
||||
this._updateOutOfOfficeReadMoreLessButton();
|
||||
|
||||
return $.when(superDef, composerDef);
|
||||
return Promise.all([superDef, composerDef]);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -241,7 +241,7 @@ var ThreadWindow = AbstractThreadWindow.extend({
|
||||
autoFocus: true,
|
||||
source: function (request, response) {
|
||||
self.call('mail_service', 'searchPartner', request.term, 10)
|
||||
.done(response);
|
||||
.then(response);
|
||||
},
|
||||
select: function (event, ui) {
|
||||
// remember partner ID so that we can replace this window
|
||||
|
||||
@@ -19,7 +19,7 @@ var ActivityModel = AbstractModel.extend({
|
||||
* @override
|
||||
* @param {Object} params
|
||||
* @param {Array[]} params.domain
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
load: function (params) {
|
||||
this.domain = params.domain;
|
||||
@@ -31,7 +31,7 @@ var ActivityModel = AbstractModel.extend({
|
||||
* @param {any} handle
|
||||
* @param {Object} params
|
||||
* @param {Array[]} params.domain
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
reload: function (handle, params) {
|
||||
if (params && 'domain' in params) {
|
||||
@@ -48,7 +48,7 @@ var ActivityModel = AbstractModel.extend({
|
||||
* Fetch activity data.
|
||||
*
|
||||
* @private
|
||||
* @returns {Deferred}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_fetchData: function () {
|
||||
var self = this;
|
||||
|
||||
@@ -5,8 +5,8 @@ var AbstractRenderer = require('web.AbstractRenderer');
|
||||
var core = require('web.core');
|
||||
var field_registry = require('web.field_registry');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var ActivityRenderer = AbstractRenderer.extend({
|
||||
className: 'o_activity_view',
|
||||
@@ -125,21 +125,22 @@ var ActivityRenderer = AbstractRenderer.extend({
|
||||
};
|
||||
var KanbanActivity = field_registry.get('kanban_activity');
|
||||
var widget = new KanbanActivity(self, "activity_ids", record, {});
|
||||
widget.appendTo($td);
|
||||
// replace clock by closest deadline
|
||||
var $date = $('<div>');
|
||||
var formated_date = moment(activity_group.o_closest_deadline).format('ll');
|
||||
var current_year = (new Date()).getFullYear();
|
||||
if (formated_date.endsWith(current_year)) { // Dummy logic to remove year (only if current year), we will maybe need to improve it
|
||||
formated_date = formated_date.slice(0, -4);
|
||||
formated_date = formated_date.replace(/( |,)*$/g, "");
|
||||
}
|
||||
$date
|
||||
.text(formated_date)
|
||||
.addClass('o_closest_deadline');
|
||||
$td.find('a')
|
||||
.empty()
|
||||
.append($date);
|
||||
widget.appendTo($td).then(function() {
|
||||
// replace clock by closest deadline
|
||||
var $date = $('<div>');
|
||||
var formated_date = moment(activity_group.o_closest_deadline).format('ll');
|
||||
var current_year = (new Date()).getFullYear();
|
||||
if (formated_date.endsWith(current_year)) { // Dummy logic to remove year (only if current year), we will maybe need to improve it
|
||||
formated_date = formated_date.slice(0, -4);
|
||||
formated_date = formated_date.replace(/( |,)*$/g, "");
|
||||
}
|
||||
$date
|
||||
.text(formated_date)
|
||||
.addClass('o_closest_deadline');
|
||||
$td.find('a')
|
||||
.empty()
|
||||
.append($date);
|
||||
});
|
||||
return $td;
|
||||
});
|
||||
var $tr = $('<tr/>', {class: 'o_data_row'})
|
||||
|
||||
@@ -162,19 +162,19 @@ QUnit.module('activity view', {
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('activity view: simple activity rendering', function (assert) {
|
||||
QUnit.test('activity view: simple activity rendering', async function (assert) {
|
||||
assert.expect(7);
|
||||
var activity = createView({
|
||||
var activity = await createView({
|
||||
View: ActivityView,
|
||||
model: 'task',
|
||||
data: this.data,
|
||||
arch: '<activity string="Task"/>',
|
||||
mockRPC: function(route, args) {
|
||||
if (args.method === 'get_activity_data') {
|
||||
return $.when(ACTIVITY_DATA);
|
||||
return Promise.resolve(ACTIVITY_DATA);
|
||||
}
|
||||
return this._super(route, args);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsOnce(activity, 'table',
|
||||
@@ -194,16 +194,16 @@ QUnit.test('activity view: simple activity rendering', function (assert) {
|
||||
activity.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('activity view: no content rendering', function (assert) {
|
||||
QUnit.test('activity view: no content rendering', async function (assert) {
|
||||
assert.expect(2);
|
||||
var activity = createView({
|
||||
var activity = await createView({
|
||||
View: ActivityView,
|
||||
model: 'task',
|
||||
data: this.data,
|
||||
arch: '<activity string="Task"/>',
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'get_activity_data') {
|
||||
return $.when({ activity_types: [] });
|
||||
return Promise.resolve({ activity_types: [] });
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -218,20 +218,20 @@ QUnit.test('activity view: no content rendering', function (assert) {
|
||||
activity.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('activity view: batch send mail on activity', function (assert) {
|
||||
QUnit.test('activity view: batch send mail on activity', async function (assert) {
|
||||
assert.expect(6);
|
||||
var activity = createView({
|
||||
var activity = await createView({
|
||||
View: ActivityView,
|
||||
model: 'task',
|
||||
data: this.data,
|
||||
arch: '<activity string="Task"/>',
|
||||
mockRPC: function(route, args) {
|
||||
if (args.method === 'activity_send_mail'){
|
||||
assert.step(args.args);
|
||||
return $.when();
|
||||
assert.step(JSON.stringify(args.args));
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'get_activity_data') {
|
||||
return $.when(ACTIVITY_DATA);
|
||||
return Promise.resolve(ACTIVITY_DATA);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -250,16 +250,16 @@ QUnit.test('activity view: batch send mail on activity', function (assert) {
|
||||
testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) i.fa-ellipsis-v'));
|
||||
testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show .o_send_mail_template:nth-child(2):contains(Task: Reception Acknowledgment)'));
|
||||
assert.verifySteps([
|
||||
[[13, 30], 9], //send mail template 9 on tasl 13 and 30
|
||||
[[13, 30], 8] //send mail template 8 on tasl 13 and 30
|
||||
'[[13,30],9]', // send mail template 9 on tasks 13 and 30
|
||||
'[[13,30],8]', // send mail template 8 on tasks 13 and 30
|
||||
]);
|
||||
|
||||
activity.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('activity view: activity widget', function (assert) {
|
||||
QUnit.test('activity view: activity widget', async function (assert) {
|
||||
assert.expect(16);
|
||||
var activity = createView({
|
||||
var activity = await createView({
|
||||
View: ActivityView,
|
||||
model: 'task',
|
||||
data: this.data,
|
||||
@@ -268,16 +268,16 @@ QUnit.test('activity view: activity widget', function (assert) {
|
||||
if (args.method === 'activity_send_mail'){
|
||||
assert.deepEqual([[30],8],args.args, "Should send template 8 on record 30");
|
||||
assert.step('activity_send_mail');
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'action_feedback_schedule_next'){
|
||||
assert.deepEqual([[3]],args.args, "Should execute action_feedback_schedule_next on activity 3 only ");
|
||||
assert.equal(args.kwargs.feedback, "feedback2");
|
||||
assert.step('action_feedback_schedule_next');
|
||||
return $.when({serverGeneratedAction: true});
|
||||
return Promise.resolve({serverGeneratedAction: true});
|
||||
}
|
||||
if (args.method === 'get_activity_data') {
|
||||
return $.when(ACTIVITY_DATA);
|
||||
return Promise.resolve(ACTIVITY_DATA);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
@@ -305,12 +305,12 @@ QUnit.test('activity view: activity widget', function (assert) {
|
||||
assert.step("Unexpected action");
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
var today = activity.$('table tbody tr:first td:nth-child(2).today');
|
||||
var dropdown = today.find('.dropdown-menu.o_activity');
|
||||
|
||||
testUtils.dom.click(today.find('.o_closest_deadline'));
|
||||
await testUtils.dom.click(today.find('.o_closest_deadline'));
|
||||
assert.hasClass(dropdown,'show', "dropdown should be displayed");
|
||||
assert.ok(dropdown.find('.o_activity_color_today:contains(Today)').length, "Title should be today");
|
||||
assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first div:contains(template8)').length,
|
||||
@@ -318,20 +318,20 @@ QUnit.test('activity view: activity widget', function (assert) {
|
||||
assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:eq(1) div:contains(template9)').length,
|
||||
"template9 should be available");
|
||||
|
||||
testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_preview'));
|
||||
testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_send'));
|
||||
await testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_preview'));
|
||||
await testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_send'));
|
||||
var overdue = activity.$('table tbody tr:first td:nth-child(3).overdue');
|
||||
testUtils.dom.click(overdue.find('.o_closest_deadline'));
|
||||
await testUtils.dom.click(overdue.find('.o_closest_deadline'));
|
||||
dropdown = overdue.find('.dropdown-menu.o_activity');
|
||||
assert.notOk(dropdown.find('.o_activity_title div div div:first span').length,
|
||||
"No template should be available");
|
||||
|
||||
testUtils.dom.click(dropdown.find('.o_schedule_activity'));
|
||||
testUtils.dom.click(overdue.find('.o_closest_deadline'));
|
||||
testUtils.dom.click(dropdown.find('.o_mark_as_done'));
|
||||
await testUtils.dom.click(dropdown.find('.o_schedule_activity'));
|
||||
await testUtils.dom.click(overdue.find('.o_closest_deadline'));
|
||||
await testUtils.dom.click(dropdown.find('.o_mark_as_done'));
|
||||
dropdown.find('#activity_feedback').val("feedback2");
|
||||
|
||||
testUtils.dom.click(dropdown.find('.o_activity_popover_done_next'));
|
||||
await testUtils.dom.click(dropdown.find('.o_activity_popover_done_next'));
|
||||
assert.verifySteps([
|
||||
"do_action_compose",
|
||||
"activity_send_mail",
|
||||
@@ -342,10 +342,10 @@ QUnit.test('activity view: activity widget', function (assert) {
|
||||
|
||||
activity.destroy();
|
||||
});
|
||||
QUnit.test('activity view: no group_by_menu and no time_range_menu', function (assert) {
|
||||
QUnit.test('activity view: no group_by_menu and no time_range_menu', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var actionManager = createActionManager({
|
||||
var actionManager = await createActionManager({
|
||||
actions: [{
|
||||
id: 1,
|
||||
name: 'Task Action',
|
||||
@@ -365,13 +365,13 @@ QUnit.test('activity view: no group_by_menu and no time_range_menu', function (a
|
||||
if (args.method === 'get_activity_data') {
|
||||
assert.deepEqual(args.kwargs.context, {lang: 'zz_ZZ'},
|
||||
'The context should have been passed');
|
||||
return $.when(ACTIVITY_DATA);
|
||||
return Promise.resolve(ACTIVITY_DATA);
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
|
||||
actionManager.doAction(1);
|
||||
await actionManager.doAction(1);
|
||||
|
||||
assert.containsN(actionManager, '.o_search_options .o_dropdown button:visible', 2,
|
||||
"only two elements should be available in view search");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,68 +20,64 @@ QUnit.module('Discuss in mobile', {
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('mobile basic rendering', function (assert) {
|
||||
QUnit.test('mobile basic rendering', async function (assert) {
|
||||
// This is a very basic first test for the client action. However, with
|
||||
// the chat_service, it is hard to override RPCs (for instance, the
|
||||
// /mail/client_action route is always called when the test suite is
|
||||
// launched), and we must wait for this RPC to be done before starting to
|
||||
// test the interface. This should be refactored to facilitate the testing.
|
||||
assert.expect(16);
|
||||
var done = assert.async();
|
||||
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
data: this.data,
|
||||
services: this.services,
|
||||
debug: true,
|
||||
}).then(function (discuss) {
|
||||
// test basic rendering in mobile
|
||||
assert.containsOnce(discuss, '.o_mail_discuss_content .o_mail_no_content',
|
||||
"should display the no content message");
|
||||
assert.containsOnce(discuss, '.o_mail_mobile_tabs',
|
||||
"should have rendered the tabs");
|
||||
assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=mailbox_inbox]'),'active',
|
||||
"should be in inbox tab");
|
||||
assert.containsOnce(discuss, '.o_mail_discuss_mobile_mailboxes_buttons:visible',
|
||||
"inbox/starred buttons should be visible");
|
||||
assert.hasClass(discuss.$('.o_mail_discuss_mobile_mailboxes_buttons .o_mailbox_inbox_item[data-type=mailbox_inbox]'),'btn-primary',
|
||||
"should be in inbox");
|
||||
assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none',
|
||||
"should have invisible button 'New Message'");
|
||||
assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none',
|
||||
"should have invisible button 'New Channel'");
|
||||
|
||||
// move to 'Chat' tab
|
||||
testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]'));
|
||||
assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]'),'active',
|
||||
"should be in 'Chat' tab");
|
||||
assert.containsNone(discuss, '.o_mail_discuss_content .o_mail_no_content',
|
||||
"should display the no content message");
|
||||
assert.strictEqual($('.o_mail_discuss_button_dm_chat').length, 1,
|
||||
"should have a button to open DM chat in 'Chat' tab");
|
||||
assert.doesNotHaveClass($('.o_mail_discuss_button_dm_chat'), 'd-none',
|
||||
"should be visible in 'Chat' tab");
|
||||
assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none',
|
||||
"should have invisible button 'New Channel' in 'Chat' tab");
|
||||
testUtils.dom.click($('.o_mail_discuss_button_dm_chat'));
|
||||
assert.containsOnce(discuss, '.o_mail_add_thread input:visible',
|
||||
"should display the input to add a channel");
|
||||
|
||||
// move to 'Channels' tab
|
||||
testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=multi_user_channel]'));
|
||||
assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none',
|
||||
"should have invisible button 'New Message' in 'Channels' tab");
|
||||
assert.doesNotHaveClass($('.o_mail_discuss_button_multi_user_channel'), 'd-none',
|
||||
"should have visible button 'New Channel' in 'Channels' tab");
|
||||
testUtils.dom.click($('.o_mail_discuss_button_multi_user_channel'));
|
||||
assert.containsOnce(discuss, '.o_mail_add_thread input:visible',
|
||||
"should display the input to add a channel");
|
||||
|
||||
discuss.destroy();
|
||||
done();
|
||||
});
|
||||
// test basic rendering in mobile
|
||||
assert.containsOnce(discuss, '.o_mail_discuss_content .o_mail_no_content',
|
||||
"should display the no content message");
|
||||
assert.containsOnce(discuss, '.o_mail_mobile_tabs',
|
||||
"should have rendered the tabs");
|
||||
assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=mailbox_inbox]'),'active',
|
||||
"should be in inbox tab");
|
||||
assert.containsOnce(discuss, '.o_mail_discuss_mobile_mailboxes_buttons:visible',
|
||||
"inbox/starred buttons should be visible");
|
||||
assert.hasClass(discuss.$('.o_mail_discuss_mobile_mailboxes_buttons .o_mailbox_inbox_item[data-type=mailbox_inbox]'),'btn-primary',
|
||||
"should be in inbox");
|
||||
assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none',
|
||||
"should have invisible button 'New Message'");
|
||||
assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none',
|
||||
"should have invisible button 'New Channel'");
|
||||
|
||||
// move to 'Chat' tab
|
||||
await testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]'));
|
||||
assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]'),'active',
|
||||
"should be in 'Chat' tab");
|
||||
assert.containsNone(discuss, '.o_mail_discuss_content .o_mail_no_content',
|
||||
"should display the no content message");
|
||||
assert.strictEqual($('.o_mail_discuss_button_dm_chat').length, 1,
|
||||
"should have a button to open DM chat in 'Chat' tab");
|
||||
assert.doesNotHaveClass($('.o_mail_discuss_button_dm_chat'), 'd-none',
|
||||
"should be visible in 'Chat' tab");
|
||||
assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none',
|
||||
"should have invisible button 'New Channel' in 'Chat' tab");
|
||||
await testUtils.dom.click($('.o_mail_discuss_button_dm_chat'));
|
||||
assert.containsOnce(discuss, '.o_mail_add_thread input:visible',
|
||||
"should display the input to add a channel");
|
||||
|
||||
// move to 'Channels' tab
|
||||
await testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=multi_user_channel]'));
|
||||
assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none',
|
||||
"should have invisible button 'New Message' in 'Channels' tab");
|
||||
assert.doesNotHaveClass($('.o_mail_discuss_button_multi_user_channel'), 'd-none',
|
||||
"should have visible button 'New Channel' in 'Channels' tab");
|
||||
await testUtils.dom.click($('.o_mail_discuss_button_multi_user_channel') );
|
||||
assert.containsOnce(discuss, '.o_mail_add_thread input:visible',
|
||||
"should display the input to add a channel");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('on_{attach/detach}_callback', function (assert) {
|
||||
|
||||
@@ -145,7 +145,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct
|
||||
data: this.data,
|
||||
services: this.services,
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
var $moderationBox = discuss.$(
|
||||
'.o_mail_discuss_item' +
|
||||
'[data-thread-id="mailbox_moderation"]');
|
||||
@@ -156,7 +156,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct
|
||||
"the mailbox counter of the moderation mailbox should display '1'");
|
||||
|
||||
// 1. go to moderation mailbox
|
||||
testUtils.dom.click($moderationBox);
|
||||
await testUtils.dom.click($moderationBox);
|
||||
var $message = discuss.$('.o_thread_message');
|
||||
// check message
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -210,7 +210,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct
|
||||
'the moderate button "Discard" should be invisible by default');
|
||||
|
||||
// click on message moderation checkbox
|
||||
testUtils.dom.click($message.find('.moderation_checkbox'));
|
||||
await testUtils.dom.click($message.find('.moderation_checkbox'));
|
||||
assert.ok($message.find('.moderation_checkbox').prop('checked'),
|
||||
"the moderation checkbox should become checked after click");
|
||||
// check select all (disabled) / unselect all buttons (enabled)
|
||||
@@ -227,7 +227,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct
|
||||
'the moderate button "Discard" should become visible');
|
||||
|
||||
// 2. go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
$message = discuss.$('.o_thread_message');
|
||||
// check correct message
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -289,12 +289,12 @@ QUnit.test('moderator: accept pending moderation message', function (assert) {
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
// 1. go to moderation box
|
||||
var $moderationBox = discuss.$(
|
||||
'.o_mail_discuss_item' +
|
||||
'[data-thread-id="mailbox_moderation"]');
|
||||
testUtils.dom.click($moderationBox);
|
||||
await testUtils.dom.click($moderationBox);
|
||||
// check there is a message to moderate
|
||||
var $message = discuss.$('.o_thread_message');
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -304,7 +304,7 @@ QUnit.test('moderator: accept pending moderation message', function (assert) {
|
||||
assert.strictEqual($message.find('.moderation_checkbox').length, 1,
|
||||
"the message should have a moderation checkbox");
|
||||
// accept the message pending moderation
|
||||
testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="accept"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="accept"]'));
|
||||
assert.verifySteps(['moderate']);
|
||||
|
||||
// stop the fadeout animation and immediately remove the element
|
||||
@@ -313,7 +313,7 @@ QUnit.test('moderator: accept pending moderation message', function (assert) {
|
||||
"should now have no message displayed in moderation box");
|
||||
|
||||
// 2. go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
$message = discuss.$('.o_thread_message');
|
||||
// check message is there and has no moderate checkbox
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -379,12 +379,12 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
// 1. go to moderation box
|
||||
var $moderationBox = discuss.$(
|
||||
'.o_mail_discuss_item' +
|
||||
'[data-thread-id="mailbox_moderation"]');
|
||||
testUtils.dom.click($moderationBox);
|
||||
await testUtils.dom.click($moderationBox);
|
||||
// check there is a message to moderate
|
||||
var $message = discuss.$('.o_thread_message');
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -394,7 +394,7 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio
|
||||
assert.strictEqual($message.find('.moderation_checkbox').length, 1,
|
||||
"the message should have a moderation checkbox");
|
||||
// reject the message pending moderation
|
||||
testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="reject"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="reject"]'));
|
||||
|
||||
// check reject dialog prompt
|
||||
assert.strictEqual($('.modal-dialog').length, 1,
|
||||
@@ -419,7 +419,7 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio
|
||||
"should have a send button on the reject dialog");
|
||||
|
||||
// send mesage
|
||||
testUtils.dom.click($('.modal-footer button'));
|
||||
await testUtils.dom.click($('.modal-footer button'));
|
||||
assert.verifySteps(['moderate']);
|
||||
|
||||
// // stop the fadeout animation and immediately remove the element
|
||||
@@ -428,7 +428,7 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio
|
||||
"should now have no message displayed in moderation box");
|
||||
|
||||
// 2. go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
assert.containsNone(discuss, '.o_thread_message')
|
||||
|
||||
discuss.destroy();
|
||||
@@ -482,12 +482,12 @@ QUnit.test('moderator: discard pending moderation message (reject without explan
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
// 1. go to moderation box
|
||||
var $moderationBox = discuss.$(
|
||||
'.o_mail_discuss_item' +
|
||||
'[data-thread-id="mailbox_moderation"]');
|
||||
testUtils.dom.click($moderationBox);
|
||||
await testUtils.dom.click($moderationBox);
|
||||
// check there is a message to moderate
|
||||
var $message = discuss.$('.o_thread_message');
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -497,7 +497,7 @@ QUnit.test('moderator: discard pending moderation message (reject without explan
|
||||
assert.strictEqual($message.find('.moderation_checkbox').length, 1,
|
||||
"the message should have a moderation checkbox");
|
||||
// discard the message pending moderation
|
||||
testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="discard"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="discard"]'));
|
||||
|
||||
// check discard dialog prompt
|
||||
assert.strictEqual($('.modal-dialog').length, 1,
|
||||
@@ -513,7 +513,7 @@ QUnit.test('moderator: discard pending moderation message (reject without explan
|
||||
"should have a cancel button in the dialog for discard");
|
||||
|
||||
// discard mesage
|
||||
testUtils.dom.click($('.modal-footer button.btn-primary'));
|
||||
await testUtils.dom.click($('.modal-footer button.btn-primary'));
|
||||
assert.verifySteps(['moderate']);
|
||||
|
||||
// stop the fadeout animation and immediately remove the element
|
||||
@@ -522,7 +522,7 @@ QUnit.test('moderator: discard pending moderation message (reject without explan
|
||||
"should now have no message displayed in moderation box");
|
||||
|
||||
// 2. go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
assert.containsNone(discuss, '.o_thread_message')
|
||||
|
||||
discuss.destroy();
|
||||
@@ -534,7 +534,7 @@ QUnit.test('author: send message in moderated channel', function (assert) {
|
||||
assert.expect(4);
|
||||
var done = assert.async();
|
||||
|
||||
var messagePostDef = $.Deferred();
|
||||
var messagePostDef = testUtils.makeTestPromise();
|
||||
|
||||
this.data.initMessaging = {
|
||||
channel_slots: {
|
||||
@@ -554,7 +554,7 @@ QUnit.test('author: send message in moderated channel', function (assert) {
|
||||
params: {},
|
||||
data: this.data,
|
||||
services: this.services,
|
||||
mockRPC: function (route, args) {
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === 'message_post') {
|
||||
var message = {
|
||||
id: 100,
|
||||
@@ -572,9 +572,10 @@ QUnit.test('author: send message in moderated channel', function (assert) {
|
||||
};
|
||||
var notification = [metaData, notifData];
|
||||
objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
messagePostDef.resolve();
|
||||
return $.when(message.id);
|
||||
return Promise.resolve(message.id);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -582,14 +583,14 @@ QUnit.test('author: send message in moderated channel', function (assert) {
|
||||
partner_id: 2,
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
objectDiscuss = discuss;
|
||||
|
||||
// go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
// post a message
|
||||
discuss.$('.o_composer_input textarea').first().val("some text");
|
||||
testUtils.dom.click(discuss.$('.o_composer_send button'));
|
||||
await testUtils.dom.click(discuss.$('.o_composer_send button'));
|
||||
|
||||
messagePostDef
|
||||
.then(function () {
|
||||
@@ -646,10 +647,10 @@ QUnit.test('author: sent message accepted in moderated channel', function (asser
|
||||
partner_id: 2,
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
|
||||
// go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
// check message is pending
|
||||
var $message = discuss.$('.o_thread_message');
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -674,7 +675,7 @@ QUnit.test('author: sent message accepted in moderated channel', function (asser
|
||||
var metaData = [dbName, 'mail.channel'];
|
||||
var notification = [metaData, messageData];
|
||||
discuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
|
||||
await testUtils.nextTick();
|
||||
// check message is accepted
|
||||
$message = discuss.$('.o_thread_message');
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -727,10 +728,10 @@ QUnit.test('author: sent message rejected in moderated channel', function (asser
|
||||
partner_id: 2,
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
.then(async function (discuss) {
|
||||
|
||||
// go to channel 'general'
|
||||
testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]'));
|
||||
// check message is pending
|
||||
var $message = discuss.$('.o_thread_message');
|
||||
assert.strictEqual($message.length, 1,
|
||||
@@ -751,7 +752,7 @@ QUnit.test('author: sent message rejected in moderated channel', function (asser
|
||||
var metaData = [dbName, 'res.partner'];
|
||||
var notification = [metaData, notifData];
|
||||
discuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
|
||||
await testUtils.nextTick();
|
||||
// // check no message
|
||||
assert.containsNone(discuss, '.o_thread_message',
|
||||
"message should be removed from channel after reject");
|
||||
@@ -761,12 +762,11 @@ QUnit.test('author: sent message rejected in moderated channel', function (asser
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('no crash when load-more fetching "accepted" message twice', function (assert) {
|
||||
QUnit.test('no crash when load-more fetching "accepted" message twice', async function (assert) {
|
||||
// This tests requires discuss not loading more messages due to having less
|
||||
// messages to fetch than available height. This justifies we simply do not
|
||||
// patch FETCH_LIMIT to 1, as it would detect that more messages could fit
|
||||
// the empty space (it behaviour is linked to "auto load more").
|
||||
var done = assert.async();
|
||||
assert.expect(2);
|
||||
|
||||
var FETCH_LIMIT = Thread.prototype._FETCH_LIMIT;
|
||||
@@ -799,7 +799,7 @@ QUnit.test('no crash when load-more fetching "accepted" message twice', function
|
||||
};
|
||||
var count = 0;
|
||||
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -811,33 +811,32 @@ QUnit.test('no crash when load-more fetching "accepted" message twice', function
|
||||
count++;
|
||||
if (count === 1) {
|
||||
// inbox message_fetch
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
// general message_fetch
|
||||
return $.when(messageData);
|
||||
return Promise.resolve(messageData);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
})
|
||||
.then(function (discuss) {
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.strictEqual($general.attr('title'), 'general',
|
||||
"should have the title 'general'");
|
||||
|
||||
// click on general
|
||||
$general.click();
|
||||
|
||||
// simulate search
|
||||
discuss.trigger_up('search', {
|
||||
domains: [['author_id', '=', 100]],
|
||||
});
|
||||
|
||||
discuss.destroy();
|
||||
done();
|
||||
});
|
||||
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.strictEqual($general.attr('title'), 'general',
|
||||
"should have the title 'general'");
|
||||
|
||||
// click on general
|
||||
await testUtils.dom.click($general);
|
||||
|
||||
// simulate search
|
||||
discuss.trigger_up('search', {
|
||||
domains: [['author_id', '=', 100]],
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -153,15 +153,14 @@ QUnit.module('Discuss (Typing Notifications)', {
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('notify is typing', function (assert) {
|
||||
QUnit.test('notify is typing', async function (assert) {
|
||||
assert.expect(11);
|
||||
var done = assert.async();
|
||||
|
||||
// this is to track the step that we are testing in the `notify_typing`
|
||||
// mocked RPC
|
||||
var step = 1;
|
||||
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -188,44 +187,42 @@ QUnit.test('notify is typing', function (assert) {
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.hasAttrValue($general, 'title', 'general',
|
||||
"should have the title 'general'");
|
||||
|
||||
// click on general
|
||||
testUtils.dom.click($general);
|
||||
|
||||
var $input = discuss.$('textarea.o_composer_text_field').first();
|
||||
assert.ok($input.length, "should display a composer input");
|
||||
|
||||
// STEP 1: current user is typing something
|
||||
step = 1;
|
||||
$input.focus();
|
||||
testUtils.fields.editInput($input, '1');
|
||||
|
||||
assert.verifySteps(['notify_typing']);
|
||||
|
||||
// STEP 2: current user clears input
|
||||
step = 2;
|
||||
testUtils.fields.editInput($input, '');
|
||||
|
||||
assert.verifySteps(['notify_typing', 'notify_typing']);
|
||||
|
||||
discuss.destroy();
|
||||
done();
|
||||
});
|
||||
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.hasAttrValue($general, 'title', 'general',
|
||||
"should have the title 'general'");
|
||||
|
||||
// click on general
|
||||
await testUtils.dom.click($general);
|
||||
|
||||
var $input = discuss.$('textarea.o_composer_text_field').first();
|
||||
assert.ok($input.length, "should display a composer input");
|
||||
|
||||
// STEP 1: current user is typing something
|
||||
step = 1;
|
||||
$input.focus();
|
||||
testUtils.fields.editInput($input, '1');
|
||||
|
||||
assert.verifySteps(['notify_typing']);
|
||||
|
||||
// STEP 2: current user clears input
|
||||
step = 2;
|
||||
testUtils.fields.editInput($input, '');
|
||||
|
||||
assert.verifySteps(['notify_typing']);
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('receive is typing notification', function (assert) {
|
||||
QUnit.test('receive is typing notification', async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var self = this;
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -235,7 +232,7 @@ QUnit.test('receive is typing notification', function (assert) {
|
||||
// when receiving an 'is typing' notification, fetch the list of
|
||||
// members of this channel if we haven't done yet.
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{ id: self.myPartnerID, name: self.myName },
|
||||
{ id: 42, name: "Someone" },
|
||||
]);
|
||||
@@ -243,67 +240,68 @@ QUnit.test('receive is typing notification', function (assert) {
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.hasAttrValue($general, 'title', 'general',
|
||||
"should have the title 'general'");
|
||||
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
// click on general
|
||||
testUtils.dom.click($general);
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should no longer show someone is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone is typing...",
|
||||
"should show who is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone is typing...",
|
||||
"should show who is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: false,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should no longer show someone is typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.hasAttrValue($general, 'title', 'general',
|
||||
"should have the title 'general'");
|
||||
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
// click on general
|
||||
await testUtils.dom.click($general);
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should no longer show someone is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone is typing...",
|
||||
"should show who is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone is typing...",
|
||||
"should show who is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: false,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should no longer show someone is typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('receive message of someone that was typing something', function (assert) {
|
||||
QUnit.test('receive message of someone that was typing something', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var self = this;
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -313,7 +311,7 @@ QUnit.test('receive message of someone that was typing something', function (ass
|
||||
// when receiving an 'is typing' notification, fetch the list of
|
||||
// members of this channel if we haven't done yet.
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{ id: self.myPartnerID, name: self.myName },
|
||||
{ id: 42, name: "Someone" },
|
||||
]);
|
||||
@@ -321,62 +319,63 @@ QUnit.test('receive message of someone that was typing something', function (ass
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]');
|
||||
testUtils.dom.click($general);
|
||||
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone is typing...",
|
||||
"should show who is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone is typing...",
|
||||
"should show who is typing on the composer of active thread");
|
||||
|
||||
// Simulate receive message from the person typing something
|
||||
var messageData = {
|
||||
author_id: [42, 'Someone'],
|
||||
body: "<p>test</p>",
|
||||
channel_ids: [1],
|
||||
id: 101,
|
||||
model: 'mail.channel',
|
||||
res_id: 1,
|
||||
};
|
||||
var notification = [[false, 'mail.channel', 1], messageData];
|
||||
discuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss, after receiving message");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should no longer show someone is typing on the composer of active thread, after receiving message");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]');
|
||||
await testUtils.dom.click($general);
|
||||
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone is typing...",
|
||||
"should show who is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone is typing...",
|
||||
"should show who is typing on the composer of active thread");
|
||||
|
||||
// Simulate receive message from the person typing something
|
||||
var messageData = {
|
||||
author_id: [42, 'Someone'],
|
||||
body: "<p>test</p>",
|
||||
channel_ids: [1],
|
||||
id: 101,
|
||||
model: 'mail.channel',
|
||||
res_id: 1,
|
||||
};
|
||||
var notification = [[false, 'mail.channel', 1], messageData];
|
||||
await discuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss, after receiving message");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should no longer show someone is typing on the composer of active thread, after receiving message");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('do not display myself as typing', function (assert) {
|
||||
QUnit.test('do not display myself as typing', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var self = this;
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -386,40 +385,39 @@ QUnit.test('do not display myself as typing', function (assert) {
|
||||
// when receiving an 'is typing' notification, fetch the list of
|
||||
// members of this channel if we haven't done yet.
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{ id: self.myPartnerID, name: self.myName },
|
||||
]);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
testUtils.dom.click($general);
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: self.myPartnerID,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should not show current user is typing in sidebar of discuss");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should not show current user is typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
await testUtils.dom.click($general);
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: self.myPartnerID,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should not show current user is typing in sidebar of discuss");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "",
|
||||
"should not show current user is typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('several users typing something at the same time', function (assert) {
|
||||
QUnit.test('several users typing something at the same time', async function (assert) {
|
||||
// The order to display several typing partners name is not random:
|
||||
// it shows at most 2 typing partners, which are the typing partners that
|
||||
// have been typing something for the longest time.
|
||||
@@ -428,7 +426,7 @@ QUnit.test('several users typing something at the same time', function (assert)
|
||||
assert.expect(10);
|
||||
|
||||
var self = this;
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -438,7 +436,7 @@ QUnit.test('several users typing something at the same time', function (assert)
|
||||
// when receiving an 'is typing' notification, fetch the list of
|
||||
// members of this channel if we haven't done yet.
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{ id: self.myPartnerID, name: self.myName },
|
||||
{ id: 42, name: "Someone" },
|
||||
{ id: 43, name: "Anonymous" },
|
||||
@@ -448,98 +446,101 @@ QUnit.test('several users typing something at the same time', function (assert)
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
|
||||
// click on general
|
||||
testUtils.dom.click($general);
|
||||
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone is typing...",
|
||||
"should show someone is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone is typing...",
|
||||
"should show someone is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 43,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone and Anonymous are typing...",
|
||||
"should show someone and anonymous are simultaneously typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone and Anonymous are typing...",
|
||||
"should show someone and anonymous are simultaneously typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 44,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone, Anonymous and more are typing...",
|
||||
"should show 2 and more users are simultaneously typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone, Anonymous and more are typing...",
|
||||
"should show 2 and more users are simultaneously typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: false,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Anonymous and Shy Guy are typing...",
|
||||
"should no longer show than someone is typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Anonymous and Shy Guy are typing...",
|
||||
"should no longer show than someone is typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
|
||||
// click on general
|
||||
await testUtils.dom.click($general);
|
||||
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone is typing...",
|
||||
"should show someone is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone is typing...",
|
||||
"should show someone is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 43,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone and Anonymous are typing...",
|
||||
"should show someone and anonymous are simultaneously typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone and Anonymous are typing...",
|
||||
"should show someone and anonymous are simultaneously typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 44,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Someone, Anonymous and more are typing...",
|
||||
"should show 2 and more users are simultaneously typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Someone, Anonymous and more are typing...",
|
||||
"should show 2 and more users are simultaneously typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: false,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"Anonymous and Shy Guy are typing...",
|
||||
"should no longer show than someone is typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"Anonymous and Shy Guy are typing...",
|
||||
"should no longer show than someone is typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('long typing partner A and in-between short typing partner B', function (assert) {
|
||||
QUnit.test('long typing partner A and in-between short typing partner B', async function (assert) {
|
||||
// Let's suppose that A is typing a very long message. If B types a short
|
||||
// and sends it, it should display that B is typing, remove B from display
|
||||
// when receiving the message, but still keep A in the typing notification.
|
||||
assert.expect(8);
|
||||
|
||||
var self = this;
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -549,7 +550,7 @@ QUnit.test('long typing partner A and in-between short typing partner B', functi
|
||||
// when receiving an 'is typing' notification, fetch the list of
|
||||
// members of this channel if we haven't done yet.
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{ id: self.myPartnerID, name: self.myName },
|
||||
{ id: 42, name: "A" },
|
||||
{ id: 43, name: "B" },
|
||||
@@ -558,76 +559,78 @@ QUnit.test('long typing partner A and in-between short typing partner B', functi
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]');
|
||||
testUtils.dom.click($general);
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"A is typing...",
|
||||
"should show A is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"A is typing...",
|
||||
"should show A is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 43,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
// B comes after A in the display, because A is oldest typing partner.
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"A and B are typing...",
|
||||
"should show A and B are typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"A and B are typing...",
|
||||
"should show A and B are typing on the composer of active thread");
|
||||
|
||||
// Simulate receive message from B
|
||||
var messageData = {
|
||||
author_id: [43, 'Someone'],
|
||||
body: "<p>test</p>",
|
||||
channel_ids: [1],
|
||||
id: 101,
|
||||
model: 'mail.channel',
|
||||
res_id: 1,
|
||||
};
|
||||
var notification = [[false, 'mail.channel', 1], messageData];
|
||||
discuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"A is typing...",
|
||||
"should show A is still typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"A is typing...",
|
||||
"should show A is still typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
// click on general channel
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]');
|
||||
await testUtils.dom.click($general);
|
||||
// pick 1st composer (basic), not 2nd composer (extended, hidden)
|
||||
var $composer = discuss.$('.o_thread_composer').first();
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.containsOnce($general, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to general icon in the sidebar");
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"A is typing...",
|
||||
"should show A is typing on hover on this thread typing icon");
|
||||
assert.containsOnce($composer, '.o_composer_thread_typing',
|
||||
"should show typing info on the composer of active thread");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"A is typing...",
|
||||
"should show A is typing on the composer of active thread");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 1,
|
||||
isTyping: true,
|
||||
partnerID: 43,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
// B comes after A in the display, because A is oldest typing partner.
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"A and B are typing...",
|
||||
"should show A and B are typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"A and B are typing...",
|
||||
"should show A and B are typing on the composer of active thread");
|
||||
|
||||
// Simulate receive message from B
|
||||
var messageData = {
|
||||
author_id: [43, 'Someone'],
|
||||
body: "<p>test</p>",
|
||||
channel_ids: [1],
|
||||
id: 101,
|
||||
model: 'mail.channel',
|
||||
res_id: 1,
|
||||
};
|
||||
var notification = [[false, 'mail.channel', 1], messageData];
|
||||
await discuss.call('bus_service', 'trigger', 'notification', [notification]);
|
||||
await testUtils.nextTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title',
|
||||
"A is typing...",
|
||||
"should show A is still typing on hover on this thread typing icon");
|
||||
assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(),
|
||||
"A is typing...",
|
||||
"should show A is still typing on the composer of active thread");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('receive is typing notification from unselected thread', function (assert) {
|
||||
QUnit.test('receive is typing notification from unselected thread', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
this.data.initMessaging.channel_slots.channel_channel.push({
|
||||
@@ -637,7 +640,7 @@ QUnit.test('receive is typing notification from unselected thread', function (as
|
||||
});
|
||||
|
||||
var self = this;
|
||||
createDiscuss({
|
||||
var discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
@@ -647,7 +650,7 @@ QUnit.test('receive is typing notification from unselected thread', function (as
|
||||
// when receiving an 'is typing' notification, fetch the list of
|
||||
// members of this channel if we haven't done yet.
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{ id: self.myPartnerID, name: self.myName },
|
||||
{ id: 42, name: "Someone" },
|
||||
]);
|
||||
@@ -655,44 +658,44 @@ QUnit.test('receive is typing notification from unselected thread', function (as
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
session: { partner_id: this.myPartnerID },
|
||||
}).then(function (discuss) {
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
var $other = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=2]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.hasAttrValue($general, 'title', 'general',
|
||||
"should have the title 'general'");
|
||||
assert.strictEqual($other.length, 1,
|
||||
"should have the channel item with id 2");
|
||||
assert.hasAttrValue($other, 'title', 'other',
|
||||
"should have the title 'other'");
|
||||
|
||||
// click on general
|
||||
testUtils.dom.click($general);
|
||||
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 2,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
$other = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=2]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should not have a thread typing icon next to general icon in the sidebar");
|
||||
assert.containsOnce($other, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to other icon in the sidebar");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
var $general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
var $other = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=2]');
|
||||
assert.strictEqual($general.length, 1,
|
||||
"should have the channel item with id 1");
|
||||
assert.hasAttrValue($general, 'title', 'general',
|
||||
"should have the title 'general'");
|
||||
assert.strictEqual($other.length, 1,
|
||||
"should have the channel item with id 2");
|
||||
assert.hasAttrValue($other, 'title', 'other',
|
||||
"should have the title 'other'");
|
||||
|
||||
// click on general
|
||||
await testUtils.dom.click($general);
|
||||
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should no longer show someone is typing in sidebar of discuss");
|
||||
|
||||
self.simulateIsTyping({
|
||||
channelID: 2,
|
||||
isTyping: true,
|
||||
partnerID: 42,
|
||||
widget: discuss,
|
||||
});
|
||||
await testUtils.nextMicrotaskTick();
|
||||
|
||||
$general = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=1]');
|
||||
$other = discuss.$('.o_mail_discuss_sidebar')
|
||||
.find('.o_mail_discuss_item[data-thread-id=2]');
|
||||
assert.containsNone($general, '.o_mail_thread_typing_icon',
|
||||
"should not have a thread typing icon next to general icon in the sidebar");
|
||||
assert.containsOnce($other, '.o_mail_thread_typing_icon',
|
||||
"should have a thread typing icon next to other icon in the sidebar");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ QUnit.module('Document Thread', {
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('open a document thread in a thread window', function (assert) {
|
||||
QUnit.test('open a document thread in a thread window', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -107,13 +107,13 @@ QUnit.test('open a document thread in a thread window', function (assert) {
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
// toggle the messaging menu and open the documentThread
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_preview',
|
||||
"should display one preview");
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open the DocumentThread in a thread window");
|
||||
@@ -125,7 +125,7 @@ QUnit.test('open a document thread in a thread window', function (assert) {
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('expand a document thread window', function (assert) {
|
||||
QUnit.test('expand a document thread window', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
@@ -144,13 +144,13 @@ QUnit.test('expand a document thread window', function (assert) {
|
||||
},
|
||||
},
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
// toggle the messaging menu and open the documentThread
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_preview',
|
||||
"should display one preview");
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open the DocumentThread in a thread window");
|
||||
|
||||
@@ -158,12 +158,12 @@ QUnit.test('expand a document thread window', function (assert) {
|
||||
'Open document', "button should have correct title");
|
||||
|
||||
// click to expand
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_expand'));
|
||||
await testUtils.dom.click($('.o_thread_window .o_thread_window_expand'));
|
||||
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('post messages in a document thread window', function (assert) {
|
||||
QUnit.test('post messages in a document thread window', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
var newMessageBody = 'Some message';
|
||||
@@ -193,7 +193,7 @@ QUnit.test('post messages in a document thread window', function (assert) {
|
||||
// add the message to the fake DB
|
||||
this.data['mail.message'].records.push(newMessage);
|
||||
this.data['some.res.model'].records[0].message_ids.push(6783);
|
||||
return $.when(6783);
|
||||
return Promise.resolve(6783);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -205,73 +205,74 @@ QUnit.test('post messages in a document thread window', function (assert) {
|
||||
"should write sent message in local storage, to share info with other tabs");
|
||||
}
|
||||
}, true);
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
// toggle the messaging menu and open the documentThread
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_preview',
|
||||
"should display one preview");
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open the DocumentThread in a thread window");
|
||||
assert.strictEqual($('.o_thread_window .o_thread_message').length, 2,
|
||||
"should display 2 messages in the thread window");
|
||||
|
||||
// post a message
|
||||
$('.o_thread_window .o_composer_text_field')
|
||||
.val(newMessageBody)
|
||||
.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER}));
|
||||
|
||||
await testUtils.fields.editAndTrigger($('.o_thread_window .o_composer_text_field'), newMessageBody,
|
||||
[$.Event('keydown', {which: $.ui.keyCode.ENTER})]);
|
||||
assert.strictEqual($('.o_thread_window .o_thread_message').length, 3,
|
||||
"should display 3 messages in the thread window");
|
||||
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('open, fold, unfold and close a document thread window', function (assert) {
|
||||
QUnit.test('open, fold, unfold and close a document thread window', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
var messagingMenu = new MessagingMenu();
|
||||
testUtils.mock.addMockEnvironment(messagingMenu, {
|
||||
services: this.services,
|
||||
data: this.data,
|
||||
session: this.session,
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
testUtils.mock.intercept(messagingMenu, 'call_service', function (ev) {
|
||||
if (ev.data.service === 'local_storage' && ev.data.method === 'setItem') {
|
||||
assert.step(ev.data.args);
|
||||
assert.step(JSON.stringify(ev.data.args));
|
||||
}
|
||||
}, true);
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
// toggle the messaging menu and open the documentThread
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_preview',
|
||||
"should display one preview");
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
assert.strictEqual($('.o_thread_window').length, 1,
|
||||
"should have open the DocumentThread in a thread window");
|
||||
assert.strictEqual($('.o_thread_window .o_thread_message').length, 2,
|
||||
"should display 2 messages in the thread window");
|
||||
|
||||
// fold and unfold thread window
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
await testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
await testUtils.nextTick();
|
||||
await testUtils.dom.click($('.o_thread_window .o_thread_window_title'));
|
||||
await testUtils.nextTick();
|
||||
|
||||
// close thread window
|
||||
testUtils.dom.click($('.o_thread_window .o_thread_window_close'));
|
||||
await testUtils.dom.click($('.o_thread_window .o_thread_window_close'));
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.verifySteps([
|
||||
['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "open"}}],
|
||||
['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "folded"}}],
|
||||
['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "open"}}],
|
||||
['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "closed"}}],
|
||||
'["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"open"}}]',
|
||||
'["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"folded"}}]',
|
||||
'["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"open"}}]',
|
||||
'["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"closed"}}]',
|
||||
]);
|
||||
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('do not open thread window on fetch message failure', function (assert) {
|
||||
QUnit.test('do not open thread window on fetch message failure', async function (assert) {
|
||||
// this may happen when the user receives a notification from a document
|
||||
// that he does not have access rights at the moment.
|
||||
assert.expect(4);
|
||||
@@ -284,17 +285,17 @@ QUnit.test('do not open thread window on fetch message failure', function (asser
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'read' && args.model === 'some.res.model' && args.args[0][0] === 1) {
|
||||
assert.step('some.res.model/1/read');
|
||||
return $.Deferred().reject(); // simulate failure
|
||||
return Promise.reject(); // simulate failure
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
messagingMenu.appendTo($('#qunit-fixture'));
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
await testUtils.dom.click(messagingMenu.$('.dropdown-toggle'));
|
||||
assert.containsOnce(messagingMenu, '.o_mail_preview');
|
||||
|
||||
testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
await testUtils.dom.click(messagingMenu.$('.o_mail_preview'));
|
||||
assert.verifySteps(['some.res.model/1/read']);
|
||||
assert.strictEqual($('.o_thread_window').length, 0,
|
||||
"should not have open the DocumentThread in a thread window on fetch messages failure");
|
||||
|
||||
@@ -20,16 +20,16 @@ var createViewer = function (params) {
|
||||
|
||||
var mockRPC = function (route) {
|
||||
if (route === '/web/static/lib/pdfjs/web/viewer.html?file=/web/content/1?model%3Dir.attachment') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === 'https://www.youtube.com/embed/FYqW0Gdwbzk') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === '/web/content/4?model%3Dir.attachment') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (route === '/web/image/6?unique=1&signature=999&model=ir.attachment') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
testUtils.mock.addMockEnvironment(parent, {
|
||||
@@ -51,14 +51,15 @@ var createViewer = function (params) {
|
||||
$target = $('body');
|
||||
$target.addClass('debug');
|
||||
}
|
||||
viewer.appendTo($target);
|
||||
|
||||
// actually destroy the parent when the viewer is destroyed
|
||||
viewer.destroy = function () {
|
||||
delete viewer.destroy;
|
||||
parent.destroy();
|
||||
};
|
||||
return viewer;
|
||||
return viewer.appendTo($target).then(function() {
|
||||
return viewer;
|
||||
});
|
||||
};
|
||||
|
||||
QUnit.module('DocumentViewer', {
|
||||
@@ -74,10 +75,10 @@ QUnit.module('DocumentViewer', {
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.test('basic rendering', function (assert) {
|
||||
QUnit.test('basic rendering', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 1,
|
||||
attachments: this.attachments,
|
||||
});
|
||||
@@ -100,11 +101,11 @@ QUnit.module('DocumentViewer', {
|
||||
viewer.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Document Viewer Youtube', function (assert) {
|
||||
QUnit.test('Document Viewer Youtube', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var youtubeURL = 'https://www.youtube.com/embed/FYqW0Gdwbzk';
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 2,
|
||||
attachments: this.attachments,
|
||||
mockRPC: function (route) {
|
||||
@@ -123,10 +124,10 @@ QUnit.module('DocumentViewer', {
|
||||
viewer.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Document Viewer html/(txt)', function (assert) {
|
||||
QUnit.test('Document Viewer html/(txt)', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 4,
|
||||
attachments: this.attachments,
|
||||
});
|
||||
@@ -139,10 +140,10 @@ QUnit.module('DocumentViewer', {
|
||||
viewer.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Document Viewer mp4', function (assert) {
|
||||
QUnit.test('Document Viewer mp4', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 5,
|
||||
attachments: this.attachments,
|
||||
});
|
||||
@@ -155,10 +156,10 @@ QUnit.module('DocumentViewer', {
|
||||
viewer.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('Document Viewer jpg', function (assert) {
|
||||
QUnit.test('Document Viewer jpg', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 6,
|
||||
attachments: this.attachments,
|
||||
});
|
||||
@@ -171,10 +172,10 @@ QUnit.module('DocumentViewer', {
|
||||
viewer.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('is closable by button', function (assert) {
|
||||
QUnit.test('is closable by button', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 6,
|
||||
attachments: this.attachments,
|
||||
});
|
||||
@@ -184,15 +185,15 @@ QUnit.module('DocumentViewer', {
|
||||
assert.containsOnce(viewer, '.o_close_btn',
|
||||
"should have a close button");
|
||||
|
||||
testUtils.dom.click(viewer.$('.o_close_btn'));
|
||||
await testUtils.dom.click(viewer.$('.o_close_btn'));
|
||||
|
||||
assert.ok(viewer.isDestroyed(), 'viewer should be destroyed');
|
||||
});
|
||||
|
||||
QUnit.test('is closable by clicking on the wrapper', function (assert) {
|
||||
QUnit.test('is closable by clicking on the wrapper', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var viewer = createViewer({
|
||||
var viewer = await createViewer({
|
||||
attachmentID: 6,
|
||||
attachments: this.attachments,
|
||||
});
|
||||
@@ -202,7 +203,7 @@ QUnit.module('DocumentViewer', {
|
||||
assert.containsOnce(viewer, '.o_viewer_img_wrapper',
|
||||
"should have a wrapper");
|
||||
|
||||
testUtils.dom.click(viewer.$('.o_viewer_img_wrapper'));
|
||||
await testUtils.dom.click(viewer.$('.o_viewer_img_wrapper'));
|
||||
|
||||
assert.ok(viewer.isDestroyed(), 'viewer should be destroyed');
|
||||
});
|
||||
|
||||
@@ -183,29 +183,29 @@ MockServer.include({
|
||||
_performRpc: function (route, args) {
|
||||
// routes
|
||||
if (route === '/mail/init_messaging') {
|
||||
return $.when(this._mockInitMessaging(args));
|
||||
return Promise.resolve(this._mockInitMessaging(args));
|
||||
}
|
||||
// methods
|
||||
if (args.method === 'channel_fetch_listeners') {
|
||||
return $.when([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
if (args.method === 'channel_fetch_preview') {
|
||||
return $.when(this._mockChannelFetchPreview(args));
|
||||
return Promise.resolve(this._mockChannelFetchPreview(args));
|
||||
}
|
||||
if (args.method === 'channel_minimize') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'channel_seen') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'channel_fetched') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'message_fetch') {
|
||||
return $.when(this._mockMessageFetch(args));
|
||||
return Promise.resolve(this._mockMessageFetch(args));
|
||||
}
|
||||
if (args.method === 'message_format') {
|
||||
return $.when(this._mockMessageFormat(args));
|
||||
return Promise.resolve(this._mockMessageFormat(args));
|
||||
}
|
||||
if (args.method === 'activity_format') {
|
||||
var res = this._mockRead(args.model, args.args, args.kwargs);
|
||||
@@ -217,19 +217,19 @@ MockServer.include({
|
||||
}
|
||||
return record;
|
||||
});
|
||||
return $.when(res);
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
if (args.method === 'set_message_done') {
|
||||
return $.when(this._mockSetMessageDone(args));
|
||||
return Promise.resolve(this._mockSetMessageDone(args));
|
||||
}
|
||||
if (args.method === 'moderate') {
|
||||
return $.when(this._mockModerate(args));
|
||||
return Promise.resolve(this._mockModerate(args));
|
||||
}
|
||||
if (args.method === 'notify_typing') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (args.method === 'set_message_done') {
|
||||
return $.when();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(route, args);
|
||||
},
|
||||
|
||||
@@ -26,9 +26,9 @@ var Widget = require('web.Widget');
|
||||
* This is async due to mail_manager/mail_service that needs to be ready.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @return {$.Promise} resolved with the discuss widget
|
||||
* @return {Promise} resolved with the discuss widget
|
||||
*/
|
||||
function createDiscuss(params) {
|
||||
async function createDiscuss(params) {
|
||||
var Parent = Widget.extend({
|
||||
do_push_state: function () {},
|
||||
});
|
||||
@@ -49,7 +49,7 @@ function createDiscuss(params) {
|
||||
parent.destroy();
|
||||
};
|
||||
|
||||
return discuss.appendTo($(selector)).then(function () {
|
||||
return discuss.appendTo($(selector)).then(function () {
|
||||
return discuss;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ QUnit.test('simple set im_status', function (assert) {
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('multi get_im_status', function (assert) {
|
||||
QUnit.test('multi get_im_status', async function (assert) {
|
||||
assert.expect(9);
|
||||
var readCount = 0;
|
||||
var parent = testUtils.createParent({
|
||||
@@ -52,7 +52,7 @@ QUnit.test('multi get_im_status', function (assert) {
|
||||
assert.deepEqual(args.args[0], [2,3]);
|
||||
assert.deepEqual(args.args[1], ['id', 'im_status']);
|
||||
readCount++;
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{id: 2, im_status: 'away'},
|
||||
{id: 3, im_status: 'im_partner'}
|
||||
]);
|
||||
@@ -64,22 +64,27 @@ QUnit.test('multi get_im_status', function (assert) {
|
||||
id: 1,
|
||||
im_status: 'online',
|
||||
}]);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 1 }), 'online');
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 2 }), undefined);
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 3 }), undefined);
|
||||
|
||||
this.timeoutMock.runPendingTimeouts();
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 1 }), 'online');
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 2 }), 'away');
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 3 }), 'im_partner');
|
||||
|
||||
this.timeoutMock.runPendingTimeouts();
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.strictEqual(readCount, 1, 'Only one read on partner should have been performed');
|
||||
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('update loop', function (assert) {
|
||||
QUnit.test('update loop', async function (assert) {
|
||||
assert.expect(12);
|
||||
var readCount = 0;
|
||||
var parent = testUtils.createParent({
|
||||
@@ -92,7 +97,7 @@ QUnit.test('update loop', function (assert) {
|
||||
assert.deepEqual(args.args[0], [1, 2]);
|
||||
assert.deepEqual(args.args[1], ['id', 'im_status']);
|
||||
readCount++;
|
||||
return $.when([
|
||||
return Promise.resolve([
|
||||
{"id": 1, "im_status": "online"},
|
||||
{"id": 2, "im_status": "away"}
|
||||
]);
|
||||
@@ -106,11 +111,14 @@ QUnit.test('update loop', function (assert) {
|
||||
{ id: 2, im_status: 'offline' },
|
||||
{ id: 3, im_status: 'im_partner' }, //shouldn't be updated !!!!
|
||||
]);
|
||||
await testUtils.nextTick();
|
||||
//_updateImStatusLoop should be running at one second per iteration, lets make a minute pass.
|
||||
assert.strictEqual(readCount, 0);
|
||||
this.timeoutMock.addTime(50*1000);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(readCount, 0);
|
||||
this.timeoutMock.addTime(1000);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(readCount, 1, 'one call should have been made after 50 seconds' );
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 1 }), 'online');
|
||||
assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 2 }), 'away');
|
||||
@@ -118,21 +126,27 @@ QUnit.test('update loop', function (assert) {
|
||||
//simulate change of focus
|
||||
//original listener: $(window).on("blur", this._onWindowFocusChange.bind(this, false); + unload, ...
|
||||
parent.call('mail_service', '_onWindowFocusChange', false); // remove focus from tab
|
||||
await testUtils.nextTick();
|
||||
|
||||
this.timeoutMock.addTime(5*60*1000); // x minutes without focus, no rpc should be done during this time
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(readCount, 1, 'No more call should have been performed');
|
||||
//simulate change of focus
|
||||
//original listener: $(window).on("focus", this._onWindowFocusChange.bind(this, true);
|
||||
parent.call('mail_service', '_onWindowFocusChange', true); // give focus to tab
|
||||
await testUtils.nextTick();
|
||||
var nextUpdateDelay = this.timeoutMock.getNextTimeoutDelay();
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(nextUpdateDelay, 1000, "next update should be done in maximum one second");
|
||||
this.timeoutMock.addTime(nextUpdateDelay); // one second should be enough
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(readCount, 2, 'One more call should have been done once tab focused');
|
||||
this.timeoutMock.runPendingTimeouts();
|
||||
await testUtils.nextTick();
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('update status', function (assert) {
|
||||
QUnit.test('update status', async function (assert) {
|
||||
// the current solution to look for updatable im_status in dom is not perfect, but waiting for
|
||||
// ability to include widgets in views, this is the most simple solution
|
||||
assert.expect(2);
|
||||
@@ -159,7 +173,7 @@ QUnit.test('update status', function (assert) {
|
||||
throw new Error(_.str.sprintf('No rpc call should be performed: %s, %s \n %s', args.model, args.method, route));
|
||||
},
|
||||
});
|
||||
statusWidget.appendTo($('#qunit-fixture'));
|
||||
await statusWidget.appendTo($('#qunit-fixture'));
|
||||
//Render unknow im_status:
|
||||
// set initial status
|
||||
assert.ok(statusWidget.$('.o_updatable_im_status i').hasClass('o_user_online'));
|
||||
@@ -167,6 +181,7 @@ QUnit.test('update status', function (assert) {
|
||||
statusWidget.call('mail_service', 'updateImStatus', [
|
||||
{ id: 1, im_status: 'offline' },
|
||||
]);
|
||||
await testUtils.nextTick();
|
||||
assert.notOk(statusWidget.$('.o_updatable_im_status i').hasClass('o_user_online'));
|
||||
this.timeoutMock.runPendingTimeouts();
|
||||
statusWidget.destroy();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user