QUnit.test('messaging menu widget: do not open chat window twice on preview clicked', async function (assert) { // This test assumes that a condition for opening chat window is to // successfully fetch messages beforehand. assert.expect(4); var self = this; // Used to pause `message_fetch` after opening the messaging menu. // This is necessary `message_fetch` on mailbox_inbox is required to // display the previews. var lockMessageFetch = false; var messageFetchDef = testUtils.makeTestPromise(); var messagingMenu = new MessagingMenu(); testUtils.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, session: { partner_id: 1 }, mockRPC: function (route, args) { if (args.method === 'message_fetch' && lockMessageFetch) { var _super = this._super.bind(this); return messageFetchDef.then(function () { assert.step('message_fetch'); return _super(route, args); }); } if (args.method === 'channel_minimize') { assert.step('channel_minimize'); // called to detach thread in chat window // simulate longpolling response with new chat window state var channelInfo = _.extend({}, self.data['mail.channel'].records[0], { is_minimized: true, state: 'open', }); var notifications = [ [['myDB', 'res.partner'], channelInfo] ]; messagingMenu.call('bus_service', 'trigger', 'notification', notifications); } return this._super.apply(this, arguments); }, }); await messagingMenu.appendTo($('#qunit-fixture')); // Opening chat window from messaging menu (pending from `messageFetchDef`) await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); lockMessageFetch = true; await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); messageFetchDef.resolve(); await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "should only display a single chat window"); assert.verifySteps([ 'message_fetch', 'channel_minimize', ], "should have fetched messages only once"); messagingMenu.destroy(); });
QUnit.test("messaging menu widget: mark as read on thread preview", function ( assert ) { assert.expect(8); testUtils.patch(DocumentThread, { markAsRead: function () { if ( this.getDocumentModel() === 'crm.lead' && this.getDocumentID() === 126 ) { assert.step('markedAsRead'); } }, }); this.data['mail.message'].records = [{ id: 10, channel_ids: ['mailbox_inbox'], res_id: 126, needaction: true, module_icon: "/crm/static/description/icon.png", date: "2018-04-05 06:37:26", subject: "Re: Interest in your Graphic Design Project", model: "crm.lead", body: "<span>Testing Messaging</span>" }]; var messagingMenu = new MessagingMenu(); testUtils.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, }); messagingMenu.appendTo($('#qunit-fixture')); messagingMenu.$('.dropdown-toggle').click(); assert.ok(messagingMenu.$el.hasClass('o_mail_systray_item'), 'should be the instance of widget'); assert.ok(messagingMenu.$el.hasClass('show'), 'MessagingMenu should be open'); var $preview = messagingMenu.$('.o_mail_preview.o_preview_unread'); assert.strictEqual($preview.length, 1, "should have one unread preview"); assert.strictEqual($preview.data('document-model'), 'crm.lead', "should preview be linked to correct document model"); assert.strictEqual($preview.data('document-id'), 126, "should preview be linked to correct document ID"); assert.strictEqual(messagingMenu.$('.o_mail_preview_mark_as_read').length, 1, "should have mark as read icon next to preview"); messagingMenu.$(".o_mail_preview_mark_as_read").click(); assert.verifySteps(['markedAsRead'], "the document thread should be marked as read"); testUtils.unpatch(DocumentThread); messagingMenu.destroy(); });
QUnit.test('dashboard intercepts custom events triggered by sub controllers', function (assert) { assert.expect(4); // we patch the ListController to force it to trigger the custom events that // we want the dashboard to intercept (to stop them or to tweak their data) testUtils.patch(ListController, { start: function () { this.trigger_up('update_filters'); this.trigger_up('env_updated'); this.do_action({}, {keepSearchView: true}); }, }); var board = createView({ View: BoardView, model: 'board', data: this.data, arch: '<form string="My Dashboard">' + '<board style="2-1">' + '<column>' + '<action context="{}" view_mode="list" string="ABC" name="51" domain="[]"></action>' + '</column>' + '</board>' + '</form>', mockRPC: function (route) { if (route === '/web/action/load') { return $.when({res_model: 'partner', views: [[false, 'list']]}); } return this._super.apply(this, arguments); }, archs: { 'partner,false,list': '<tree string="Partner"/>', }, intercepts: { do_action: function (ev) { assert.strictEqual(ev.data.options.keepSearchView, false, "the 'keepSearchView' options should have been set to false"); }, env_updated: function (ev) { assert.strictEqual(ev.target.modelName, 'board', "env_updated event should be triggered by the dashboard itself"); assert.step('env_updated'); }, update_filters: assert.step.bind(assert, 'update_filters'), }, }); assert.verifySteps([ 'env_updated', // triggered by the dashboard itself ]); testUtils.unpatch(ListController); board.destroy(); });
QUnit.test('activity menu widget: activity menu with 3 records', function (assert) { assert.expect(10); var self = this; var activityMenu = new systray.ActivityMenu(); testUtils.addMockEnvironment(activityMenu, { services: this.services, mockRPC: function (route, args) { if (args.method === 'activity_user_count') { return $.when(self.data['mail.activity.menu']['records']); } return this._super(route, args); }, }); activityMenu.appendTo($('#qunit-fixture')); assert.ok(activityMenu.$el.hasClass('o_mail_navbar_item'), 'should be the instance of widget'); assert.ok(activityMenu.$('.o_mail_channel_preview').hasClass('o_mail_channel_preview'), "should instance of widget"); assert.ok(activityMenu.$('.o_notification_counter').hasClass('o_notification_counter'), "widget should have notification counter"); assert.strictEqual(parseInt(activityMenu.el.innerText), 5, "widget should have 5 notification counter"); var context = {}; testUtils.intercept(activityMenu, 'do_action', function(event) { assert.deepEqual(event.data.action.context, context, "wrong context value"); }, true); // case 1: click on "late" context = { search_default_activities_overdue: 1, }; activityMenu.$('.dropdown-toggle').click(); assert.strictEqual(activityMenu.$el.hasClass("open"), true, 'ActivityMenu should be open'); activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='overdue']").click(); assert.strictEqual(activityMenu.$el.hasClass("open"), false, 'ActivityMenu should be closed'); // case 2: click on "today" context = { search_default_activities_today: 1, }; activityMenu.$('.dropdown-toggle').click(); activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='today']").click(); // case 3: click on "future" context = { search_default_activities_upcoming_all: 1, }; activityMenu.$('.dropdown-toggle').click(); activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='upcoming_all']").click(); // case 4: click anywere else context = { search_default_activities_overdue: 1, search_default_activities_today: 1, }; activityMenu.$('.dropdown-toggle').click(); activityMenu.$(".o_mail_navbar_dropdown_channels > div[data-model_name='Issue']").click(); activityMenu.destroy(); });
QUnit.test('media dialog: icon', async function (assert) { assert.expect(1); var form = await testUtils.createView({ View: FormView, model: 'note.note', data: this.data, arch: '<form>' + '<field name="body" widget="html" style="height: 100px"/>' + '</form>', res_id: 1, mockRPC: function (route, args) { if (args.model === 'ir.attachment') { return Promise.resolve([]); } return this._super(route, args); }, }); await testUtils.form.clickEdit(form); var $field = form.$('.oe_form_field[name="body"]'); // the dialog load some xml assets var defMediaDialog = testUtils.makeTestPromise(); testUtils.mock.patch(MediaDialog, { init: function () { this._super.apply(this, arguments); this.opened(defMediaDialog.resolve.bind(defMediaDialog)); } }); var pText = $field.find('.note-editable p').first().contents()[0]; Wysiwyg.setRange(pText, 1); $field.find('.note-toolbar .note-insert button:has(.fa-file-image-o)').mousedown().click(); // load static xml file (dialog, media dialog, unsplash image widget) await defMediaDialog; $('.modal .tab-content .tab-pane').removeClass('fade'); // to be sync in test await testUtils.dom.click($('.modal a[aria-controls="editor-media-icon"]')); await testUtils.dom.click($('.modal #editor-media-icon .font-icons-icon.fa-glass')); await testUtils.dom.click($('.modal .modal-footer button.btn-primary')); var $editable = form.$('.oe_form_field[name="body"] .note-editable'); assert.strictEqual($editable.data('wysiwyg').getValue(), '<p>t<span class="fa fa-glass"></span>oto toto toto</p><p>tata</p>', "should have the image in the dom"); testUtils.mock.unpatch(MediaDialog); form.destroy(); });
QUnit.test("calling _rpc on destroyed widgets", async function (assert) { assert.expect(3); var def; var parent = new Widget(); testUtils.mock.addMockEnvironment(parent, { session: { rpc: function () { def = testUtils.makeTestPromise(); def.abort = def.reject; return def; }, }, services: { ajax: AjaxService }, }); var widget = new Widget(parent); widget._rpc({route: '/a/route'}).then(function () { assert.ok(true, "The ajax call should be resolve"); }); def.resolve(); await testUtils.nextMicrotaskTick(); def = null; widget._rpc({route: '/a/route'}).then(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }).catch(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }); widget.destroy(); def.resolve(); await testUtils.nextMicrotaskTick(); def = null; widget._rpc({route: '/a/route'}).then(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }).catch(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }); assert.ok(!def, "trigger_up is not performed and the call returns a " + "promise that remains pending forever"); assert.ok(true, "there should be no crash when calling _rpc on a destroyed widget"); parent.destroy(); });
QUnit.test('do no update form twice after a command barcode scanned', function (assert) { assert.expect(7); var delay = barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms; barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = 0; testUtils.patch(FormController, { update: function () { assert.step('update'); return this._super.apply(this, arguments); }, }); var form = createView({ View: FormView, model: 'product', data: this.data, arch: '<form>' + '<field name="display_name"/>' + '<field name="int_field" widget="field_float_scannable"/>' + '</form>', mockRPC: function (route, args) { if (args.method === 'read') { assert.step('read'); } return this._super.apply(this, arguments); }, res_id: 1, viewOptions: { ids: [1, 2], index: 0, }, }); assert.verifySteps(['read'], "update should not have been called yet"); // switch to next record _.each(["O","-","C","M","D",".","N","E","X","T","Enter"], triggerKeypressEvent); // 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']); _.each(['5','4','3','9','8','2','6','7','1','2','5','2','Enter'], triggerKeypressEvent); // 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']); form.destroy(); barcodeEvents.BarcodeEvents.max_time_between_keys_in_ms = delay; testUtils.unpatch(FormController); });
QUnit.test('drag&drop snippet', async function (assert) { assert.expect(1); var form = await testUtils.createView({ View: FormView, model: 'note.note', data: this.data, arch: '<form>' + '<field name="body" widget="html" style="height: 100px" options="{\'snippets\': \'web_editor.snippets\', \'cssEdit\': \'template.assets_all_style\'}"/>' + '</form>', res_id: 1, }); var defSnippets = testUtils.makeTestPromise(); testUtils.mock.intercept(form, "snippets_loaded", function () { defSnippets.resolve(); }); await testUtils.form.clickEdit(form); await defSnippets; var doc = form.$('iframe').contents()[0]; var $content = $('#iframe_target', doc); var $editable = $content.find('.note-editable'); var $hr = $content.find('.oe_snippet_thumbnail:first'); var from = $hr.offset(); var to = $editable.find('p').offset(); $hr.trigger($.Event("mousedown", { which: 1, pageX: from.left + 1, pageY: from.top + 1 })); $hr.trigger($.Event("mousemove", { which: 1, pageX: to.left, pageY: to.top })); $hr.trigger($.Event("mouseup", { which: 1, pageX: to.left, pageY: to.top })); assert.strictEqual($editable.data('wysiwyg').getValue().replace(/\s+/g, ' '), '<div class=\"s_hr pt32 pb32\"> <hr class=\"s_hr_1px s_hr_solid w-100 mx-auto\"> </div><p>toto toto toto</p><p>tata</p>', "should drop the snippet"); form.destroy(); });
QUnit.test('html_frame does not crash when saving in readonly', function (assert) { // The 'Save' action may be triggered even in readonly (e.g. when clicking // on a button in the form view) assert.expect(2); var form = testUtils.createView({ View: FormView, model: 'mass.mailing', data: this.data, arch: '<form string="Partners">' + '<sheet>' + '<field name="body" widget="html_frame" options="{\'editor_url\': \'/logo\'}"/>' + '</sheet>' + '</form>', res_id: 1, mockRPC: function (route, args) { if (args.method) { assert.step(args.method); } if (_.str.startsWith(route, '/logo')) { // manually call the callback to simulate that the iframe has // been loaded (note: just the content, not the editor) window.odoo[$.deparam(route).callback + '_content'].call(); return $.when(); } return this._super.apply(this, arguments); }, }); form.saveRecord(); // before the fix done in this commit, it crashed here assert.verifySteps(['read']); form.destroy(); });
QUnit.test('dashboard: click on a button to execute an action', function (assert) { assert.expect(2); var dashboard_data = this.dashboard_data; var kanban = createView({ View: view_registry.get('sales_team_dashboard'), model: 'partner', data: this.data, arch: '<kanban class="o_kanban_test">' + '<templates><t t-name="kanban-box">' + '<div>' + '<button name="func_name" String="A" type="object" class="my_button"/>' + '<field name="foo"/>' + '</div>' + '</t></templates>' + '</kanban>', mockRPC: function (route, args) { if (args.method === 'retrieve_sales_dashboard') { return $.when(dashboard_data); } return this._super(route, args); }, }); testUtils.intercept(kanban, 'execute_action', function (event) { assert.strictEqual(event.data.action_data.name, 'func_name', 'execute_action should have been triggered with the correct data'); assert.strictEqual(event.data.action_data.type, 'object', 'execute_action should have been triggered with the correct data'); }); kanban.$('.my_button:first()').click(); // click on the button of the first card kanban.destroy(); });
QUnit.test("list: edit view menu item", async function (assert) { assert.expect(3); var debugManager = createDebugManager(); debugManager.appendTo($('#qunit-fixture')); // Simulate update debug manager from web client var action = { views: [{ displayName: "List", fieldsView: { view_id: 1, }, type: "list", }], }; var view = { viewType: "list", }; await testUtils.nextTick(); await debugManager.update('action', action, view); var $editView = debugManager.$('a[data-action=edit][data-model="ir.ui.view"]'); assert.strictEqual($editView.length, 1, "should have edit view menu item"); assert.strictEqual($editView.text().trim(), "Edit View: List", "should have correct menu item text for editing view"); assert.strictEqual($editView.data('id'), 1, "should have correct view_id"); debugManager.destroy(); });
QUnit.test('field html widget (with options inline-style)', function (assert) { var done = assert.async(); assert.expect(3); var form = testUtils.createView({ View: FormView, model: 'mass.mailing', data: this.data, arch: '<form string="Partners">' + '<field name="body" widget="html" style="height: 100px" options="{\'style-inline\': true}"/>' + '</form>', res_id: 1, }); assert.strictEqual(form.$('iframe').length, 1, "should have rendered an iframe without crashing in readonly"); assert.strictEqual(form.$('div[name=body]').attr('style'), 'height: 100px', "should have applied the style correctly"); form.$buttons.find('.o_form_button_edit').click(); assert.strictEqual(form.$('.note-editable').html(), '<div class="field_body">yep</div>', "should have rendered the field correctly in edit"); // summernote invokes handlers after a setTimeout, so we must wait as well // before destroying the widget (otherwise we'll have a crash later on) setTimeout(function () { form.destroy(); done(); }, 0); });
QUnit.test('rendering with iframe for readonly mode', async function (assert) { assert.expect(3); var form = await testUtils.createView({ View: FormView, model: 'note.note', data: this.data, arch: '<form>' + '<field name="body" widget="html" style="height: 100px" options="{\'cssReadonly\': \'template.assets\'}"/>' + '</form>', res_id: 1, }); var $field = form.$('.oe_form_field[name="body"]'); var $iframe = $field.find('iframe.o_readonly'); await $iframe.data('loadDef'); var doc = $iframe.contents()[0]; assert.strictEqual($(doc).find('#iframe_target').html(), '<p>toto toto toto</p><p>tata</p>', "should have rendered a div with correct content in readonly"); assert.strictEqual(doc.defaultView.getComputedStyle(doc.body).backgroundColor, 'rgb(255, 0, 0)', "should load the asset css"); await testUtils.form.clickEdit(form); $field = form.$('.oe_form_field[name="body"]'); assert.strictEqual($field.find('.note-editable').html(), '<p>toto toto toto</p><p>tata</p>', "should have rendered the field correctly in edit"); form.destroy(); });
QUnit.test('Attendance Kiosk Mode Test', function (assert) { assert.expect(2); var $target = $('#qunit-fixture'); var self = this; var rpcCount = 0; var clientAction = new KioskMode(null); testUtils.addMockEnvironment(clientAction, { data: this.data, session: { uid: 1, company_id: 1, }, mockRPC: function(route, args) { if (args.method === 'attendance_scan' && args.model === 'hr.employee') { rpcCount++; return $.when(self.data['hr.employee'].records[0]); } return this._super(route, args); }, }); 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.'); core.bus.trigger('barcode_scanned', 2); assert.strictEqual(rpcCount, 1, 'RPC call should have been done only once.'); clientAction.destroy(); });
QUnit.test('field htmlsimple does not crash when commitChanges is called in mode=readonly', function (assert) { assert.expect(1); var form = testUtils.createView({ View: FormView, model: 'mass.mailing', data: this.data, arch: '<form string="Partners">' + '<header>' + '<button name="some_method" class="s" string="Do it" type="object"/>' + '</header>' + '<sheet>' + '<field name="body"/>' + '</sheet>' + '</form>', res_id: 1, intercepts: { execute_action: function () { assert.step('execute_action'); } }, }); form.$('button:contains(Do it)').click(); form.destroy(); });
QUnit.test('field html_frame widget', function (assert) { assert.expect(6); var form = testUtils.createView({ View: FormView, model: 'mass.mailing', data: this.data, arch: '<form string="Partners">' + '<field name="body" widget="html_frame" options="{\'editor_url\': \'/logo\'}"/>' + '</form>', res_id: 1, session: {user_context: {lang: "en_us"}}, mockRPC: function (route) { if (_.str.startsWith(route, '/logo')) { // those tests will be executed twice, once in readonly and once in edit assert.ok(route.search('model=mass.mailing') > 0, "the route should specify the correct model"); assert.ok(route.search('res_id=1') > 0, "the route should specify the correct id"); return $.when(); } return this._super.apply(this, arguments); }, }); assert.strictEqual(form.$('iframe').length, 1, "should have rendered an iframe without crashing"); form.$buttons.find('.o_form_button_edit').click(); assert.strictEqual(form.$('iframe').length, 1, "should have rendered an iframe without crashing"); form.destroy(); });
QUnit.test('Display a custom notification', async function (assert) { assert.expect(3); var Custom = Notification.extend({ init: function (parent, params) { this._super.apply(this, arguments); assert.ok(params.customParams, 'instantiate custom notification'); }, start: function () { var self = this; return this._super().then(function () { self.$el.html('Custom'); }); }, }); var view = await createView(this.viewParams); view.call('notification', 'notify', { Notification: Custom, customParams: true, }); await testUtils.nextMicrotaskTick(); assert.containsOnce($('body'), '.o_notification_manager .o_notification:contains(Custom)', "should display the notification"); view.destroy(); assert.containsNone($('body'), '.o_notification_manager .o_notification', "should destroy the notification"); });
QUnit.test('Display a sticky notification with onClose callback', async function (assert) { assert.expect(2); testUtils.mock.unpatch(Notification); testUtils.mock.patch(Notification, { _autoCloseDelay: 2500, _animation: false, }); var view = await createView(this.viewParams); var close = 0; view.call('notification', 'notify', { title: 'a', message: 'b', sticky: true, onClose: function () { close++; } }); await testUtils.nextMicrotaskTick(); assert.strictEqual(close, 0, "should wait to call onClose method once"); testUtils.dom.click($('body .o_notification_manager .o_notification .o_notification_close')); assert.strictEqual(close, 1, "should call onClose method once"); view.destroy(); });
QUnit.test('formviewdialog buttons in footer are positioned properly', function (assert) { assert.expect(2); var parent = createParent({ data: this.data, archs: { 'partner,false,form': '<form string="Partner">' + '<sheet>' + '<group><field name="foo"/></group>' + '<footer><button string="Custom Button" type="object" class="btn-primary"/></footer>' + '</sheet>' + '</form>', }, }); testUtils.intercept(parent, 'env_updated', function () { throw new Error("The environment should not be propagated to the action manager"); }); new dialogs.FormViewDialog(parent, { res_model: 'partner', res_id: 1, }).open(); assert.notOk($('div.modal .modal-body button').length, "should not have any button in body"); assert.strictEqual($('div.modal .modal-footer button').length, 1, "should have only one button in footer"); parent.destroy(); });
/** * Test Utils * * In this module, we define some utility functions to create mock objects * in the mail module, such as the BusService or Discuss. */ /** * Create asynchronously a discuss 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 */ function createDiscuss(params) { var Parent = Widget.extend({ do_push_state: function () {}, }); var parent = new Parent(); testUtils.addMockEnvironment(parent, _.extend(params, { archs: { 'mail.message,false,search': '<search/>', }, })); var discuss = new Discuss(parent, params); discuss.set_cp_bus(new Widget()); var selector = params.debug ? 'body' : '#qunit-fixture'; var controlPanel = new ControlPanel(parent); controlPanel.appendTo($(selector)); // override 'destroy' of discuss so that it calls 'destroy' on the parent // instead, which is the parent of discuss and the mockServer. discuss.destroy = function () { // remove the override to properly destroy discuss and its children // when it will be called the second time (by its parent) delete discuss.destroy; parent.destroy(); }; // link the view to the control panel discuss.set_cp_bus(controlPanel.get_bus()); return discuss.appendTo($(selector)).then(function () { return discuss; }); }
QUnit.test('field html translatable', async function (assert) { assert.expect(3); var multiLang = _t.database.multi_lang; _t.database.multi_lang = true; this.data['note.note'].fields.body.translate = true; var form = await testUtils.createView({ View: FormView, model: 'note.note', data: this.data, arch: '<form string="Partners">' + '<field name="body" widget="html"/>' + '</form>', res_id: 1, mockRPC: function (route, args) { if (route === '/web/dataset/call_button' && args.method === 'translate_fields') { assert.deepEqual(args.args, ['note.note', 1, 'body', {}], "should call 'call_button' route"); return Promise.resolve(); } return this._super.apply(this, arguments); }, }); assert.strictEqual(form.$('.oe_form_field_html .o_field_translate').length, 0, "should not have a translate button in readonly mode"); await testUtils.form.clickEdit(form); var $button = form.$('.oe_form_field_html .note-toolbar .o_field_translate'); assert.strictEqual($button.length, 1, "should have a translate button"); $button.click(); form.destroy(); _t.database.multi_lang = multiLang; });
function createGreetingMessage (target, barcode){ var action = { attendance: { check_in: "2018-09-20 13:41:13", employee_id: [barcode], }, next_action: "hr_attendance.hr_attendance_action_kiosk_mode", } var clientAction = new GreetingMessage(null, action); testUtils.addMockEnvironment(clientAction, { data: self.data, session: { uid: 1, company_id: 1, }, mockRPC: function(route, args) { if (args.method === 'attendance_scan' && args.model === 'hr.employee') { rpcCount++; action.attendance.employee_id = [args.args[0], 'Employee']; /* if rpc have been made, a new instance is created to simulate the same behaviour as functional flow. */ createGreetingMessage (target, args.args[0]); return $.when({action: action}); } return this._super(route, args); }, }); clientAction.appendTo(target); clientActions.push(clientAction); };
QUnit.test('messaging menu widget: messaging menu with 1 record', function (assert) { assert.expect(3); var messagingMenu = new MessagingMenu(); testUtils.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, }); messagingMenu.appendTo($('#qunit-fixture')); messagingMenu.$('.dropdown-toggle').click(); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 1, "should display a preview"); assert.strictEqual(messagingMenu.$('.o_preview_name').text().trim(), "general", "should display correct name of channel in preview"); // remove any space-character inside text var lastMessagePreviewText = messagingMenu.$('.o_last_message_preview').text().replace(/\s/g, ""); assert.strictEqual(lastMessagePreviewText, "Me:test", "should display correct last message preview in channel preview"); messagingMenu.destroy(); });
QUnit.test('html_frame saving in edit mode (editor and content fully loaded)', function (assert) { var done = assert.async(); assert.expect(4); var editorBar = new web_editor.Class(); var loadDeferred = $.Deferred(); var writeDeferred = $.Deferred(); var form = testUtils.createView({ View: FormView, model: 'mass.mailing', data: this.data, arch: '<form string="Partners">' + '<sheet>' + '<field name="display_name"/>' + '<field name="body" widget="html_frame" options="{\'editor_url\': \'/logo\'}"/>' + '</sheet>' + '</form>', res_id: 1, mockRPC: function (route, args) { if (args.method) { assert.step(args.method); if (args.method === 'write') { writeDeferred.resolve(); } } if (_.str.startsWith(route, '/logo')) { // manually call the callback to simulate that the iframe has // been fully loaded (content + editor) var callback = $.deparam(route).callback; return loadDeferred.then(function () { var contentCallback = window.odoo[callback + '_content']; var editorCallback = window.odoo[callback + '_editor']; if (editorCallback && contentCallback) { contentCallback(); editorCallback(editorBar); } }); } return this._super.apply(this, arguments); }, }); form.$buttons.find('.o_form_button_edit').click(); form.$('input').val('trululu').trigger('input'); form.$buttons.find('.o_form_button_save').click(); loadDeferred.resolve(); // simulate late loading of html frame assert.strictEqual(form.$('.o_field_char').val(), 'trululu', "should have saved the char field text"); writeDeferred.then( function () { // html_frame is async with write assert.verifySteps(['read', 'write']); form.destroy(); done(); }); });
function createFiltersMenu(filters, fields, params) { params = params || {}; var target = params.debug ? document.body : $('#qunit-fixture'); var menu = new FiltersMenu(null, filters, fields); testUtils.addMockEnvironment(menu, params); menu.appendTo(target); return menu; }
QUnit.test('needaction messages in channels should appear, in addition to channel preview', function (assert) { // Let's suppose a channel whose before-last message (msg1) is a needaction, // but not the last message (msg2). In that case, the systray messaging // menu should display the needaction message msg1 in the preview, and the // channel preview with the msg2. assert.expect(3); // simulate a needaction (mention) message in channel 'general' var partnerID = 44; var needactionMessage = { author_id: [1, "Demo"], body: "<p>@Administrator: ping</p>", channel_ids: [1], id: 3, model: 'mail.channel', needaction: true, needaction_partner_ids: [partnerID], record_name: 'general', res_id: 1, }; var lastMessage = { author_id: [2, "Other"], body: "<p>last message content</p>", channel_ids: [1], id: 4, model: 'mail.channel', record_name: 'general', res_id: 1, }; this.data['mail.message'].records = [needactionMessage, lastMessage]; var messagingMenu = new MessagingMenu(); testUtils.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, session: { partner_id: partnerID, }, }); messagingMenu.appendTo($('#qunit-fixture')); messagingMenu.$('.dropdown-toggle').click(); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 2, "should display two previews"); var $preview1 = messagingMenu.$('.o_mail_preview').eq(0); var $preview2 = messagingMenu.$('.o_mail_preview').eq(1); assert.strictEqual($preview1.find('.o_last_message_preview').text().replace(/\s/g, ""), "Demo:@Administrator:ping", "1st preview (needaction preview) should display needaction message"); assert.strictEqual($preview2.find('.o_last_message_preview').text().replace(/\s/g, ""), "Other:lastmessagecontent", "2nd preview (channel preview) should display last message preview"); messagingMenu.destroy(); });
QUnit.skip('Display a simple notification with onClose callback when automatically close', async function (assert) { assert.expect(2); var close = 0; var view = await createView(this.viewParams); view.call('notification', 'notify', { title: 'a', message: 'b', onClose: function () { close++; } }); await testUtils.nextMicrotaskTick(); view.destroy(); assert.strictEqual(close, 0, "should wait to call onClose method once"); await testUtils.nextTick(); assert.strictEqual(close, 1, "should call onClose method once"); });
QUnit.test('messaging menu widget: no crash when clicking on inbox notification not associated to a document', function (assert) { assert.expect(3); var messagingMenu = new MessagingMenu(); testUtils.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, session: { partner_id: 1, }, intercepts: { /** * Simulate action 'mail.action_discuss' successfully performed. * * @param {OdooEvent} ev * @param {function} ev.data.on_success called when success action performed */ do_action: function (ev) { ev.data.on_success(); }, }, }); messagingMenu.appendTo($('#qunit-fixture')); // Simulate received needaction message without associated document, // so that we have a message in inbox without a model and a resID var message = { id: 2, author_id: [1, "Me"], body: '<p>test</p>', channel_ids: [], needaction_partner_ids: [1], }; var notifications = [ [['myDB', 'ir.needaction'], message] ]; messagingMenu.call('bus_service', 'trigger', 'notification', notifications); // Open messaging menu messagingMenu.$('.dropdown-toggle').click(); var $firstChannelPreview = messagingMenu.$('.o_mail_preview').first(); assert.strictEqual($firstChannelPreview.length, 1, "should have at least one channel preview"); assert.strictEqual($firstChannelPreview.data('preview-id'), 'mailbox_inbox', "should be a preview from channel inbox"); try { $firstChannelPreview.click(); assert.ok(true, "should not have crashed when clicking on needaction preview message"); } finally { messagingMenu.destroy(); } });
function createParent(params) { var widget = new Widget(); widget.on('get_emojis', widget, function (ev) { ev.data.callback([]); }); testUtils.addMockEnvironment(widget, params); return widget; }
QUnit.test('do not crash when destroyed between start en end of _renderSearchView', function (assert) { assert.expect(2); var discuss; testUtils.patch(Discuss, { init: function () { discuss = this; this._super.apply(this, arguments); }, }); var def = $.Deferred(); testUtils.patch(SearchView, { willStart: function () { var result = this._super.apply(this, arguments); return def.then($.when(result)); }, }); createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, mockRPC: function (route, args) { if (args.method) { assert.step(args.method); } return this._super.apply(this, arguments); }, }); discuss.destroy(); def.resolve(); assert.verifySteps([ "load_views", ]); testUtils.unpatch(Discuss); testUtils.unpatch(SearchView); });