QUnit.test('Select a product in the list and check for template loading', async function (assert){ assert.expect(1); var product_configurator_form = await createView({ View: ProductConfiguratorFormView, model: 'sale_product_configurator', data: this.data, arch: '<form js_class="product_configurator_form">' + '<group>' + '<field name="product_template_id" class="oe_product_configurator_product_template_id" />' + '<field name="product_template_attribute_value_ids" invisible="1" />' + '<field name="product_no_variant_attribute_value_ids" invisible="1" />' + '<field name="product_custom_attribute_value_ids" invisible="1" />' + '</group>' + '<footer>' + '<button string="Add" class="btn-primary o_sale_product_configurator_add disabled"/>' + '<button string="Cancel" class="btn-secondary" special="cancel"/>' + '</footer>' + '</form>', mockRPC: function (route) { if (route === '/sale_product_configurator/configure') { assert.ok(true); return Promise.resolve('<div>plop</div>'); } return this._super.apply(this, arguments); } }); await testUtils.dom.click(product_configurator_form.$('.o_input')); await testUtils.dom.click($("ul.ui-autocomplete li a:contains('Customizable Desk')").mouseenter()); product_configurator_form.destroy(); });
function(assert) { assert.expect(3); var html = "<div>" + '<a name="a1" type="action" class="simple">simple</a>' + '<a name="a2" type="action" class="with-child">' + "<span>child</input>" + "</a>" + "</div>"; var view = createView({ View: getHtmlView(html, "test"), data: this.data, model: "test_model", arch: "<test/>", intercepts: { do_action: function(event) { assert.step(event.data.action); } } }); testUtils.dom.click(view.$(".simple")); testUtils.dom.click(view.$(".with-child span")); assert.verifySteps(["a1", "a2"]); }
QUnit.test('Pending', async function (assert) { assert.expect(7); this.data['mail.message'].records[0].snailmail_status = 'pending'; this.data['mail.message'].records[0].snailmail_error = false; var form = await createView({ View: FormView, model: 'account.invoice', res_id: 1, data: this.data, services: this.services, arch: getArch(), }); assert.containsOnce(form, '.o_thread_snailmail_tooltip_container', "Snailmail icon should appear on message"); assert.containsOnce(form, '.o_thread_message_snailmail_pending', "Snailmail status of message should be 'pending'"); assert.containsNone(form, '.o_thread_tooltip_snailmail', "No tooltip should be present"); testUtils.dom.triggerMouseEvent(form.$('.o_thread_message_snailmail_pending'), 'mouseenter'); assert.ok($('.o_thread_tooltip_snailmail:visible').length, "Tooltip should appear when hovering the Snailmail Icon"); assert.ok($('.o_thread_tooltip_snailmail_icon.fa-clock-o').length, "Tooltip should show correct icon"); assert.ok($('.o_thread_tooltip_snailmail:contains("Awaiting Dispatch")').length, "Tooltip should show correct text"); await testUtils.dom.click(form.$('.o_thread_message_snailmail_pending')); assert.containsNone($('.modal'), "No modal should open on click"); form.destroy(); });
QUnit.test('click on an item should toggle item', function (assert) { assert.expect(9); var eventNumber = 0; var dropdownMenu = createDropdownMenu(this.dropdownHeader, this.items, { intercepts: { menu_item_toggled: function (ev) { eventNumber++; assert.strictEqual(ev.data.itemId, 1); if (eventNumber === 1) { assert.strictEqual(ev.data.isActive, true); } else { assert.strictEqual(ev.data.isActive, false); } }, }, }); testUtils.dom.click(dropdownMenu.$('button:first')); assert.doesNotHaveClass(dropdownMenu.$('.o_menu_item:first > .dropdown-item'), 'selected'); testUtils.dom.click(dropdownMenu.$('.o_menu_item a').first()); assert.hasClass(dropdownMenu.$('.o_menu_item:first > .dropdown-item'), 'selected'); assert.ok(dropdownMenu.$('.o_menu_item:first').is(':visible'), 'item should still be visible'); testUtils.dom.click(dropdownMenu.$('.o_menu_item a').first()); assert.doesNotHaveClass(dropdownMenu.$('.o_menu_item:first > .dropdown-item'), 'selected'); assert.ok(dropdownMenu.$('.o_menu_item:first').is(':visible'), 'item should still be visible'); dropdownMenu.destroy(); });
QUnit.test('Select a product in the list and check for template loading', function (assert){ assert.expect(1); var product_configurator_form = createView({ View: ProductConfiguratorFormView, model: 'sale_product_configurator', data: this.data, arch: '<form js_class="product_configurator_form">' + '<group>' + '<field name="product_template_id" class="oe_product_configurator_product_template_id" />' + '</group>' + '<footer>' + '<button string="Add" class="btn-primary o_sale_product_configurator_add disabled"/>' + '<button string="Cancel" class="btn-secondary" special="cancel"/>' + '</footer>' + '</form>', mockRPC: function (route) { if (route === '/product_configurator/configure') { assert.ok(true); return $.Deferred().then(_.constant(1)); } return this._super.apply(this, arguments); } }); testUtils.dom.click(product_configurator_form.$('.o_input')); testUtils.dom.click($("ul.ui-autocomplete li a:contains('Customizable Desk')").mouseenter()); });
QUnit.test('select a groupBy of no date type in Add Custom Group menu add properly that groupBy to menu', function (assert) { assert.expect(6); var groupByMenu = createGroupByMenu( [], { fieldName: {sortable: true, name: 'candlelight', string: 'Candlelight', type: 'boolean'}, }, { intercepts: { new_groupBy: function (ev) { assert.strictEqual(ev.data.description, 'Candlelight'); assert.strictEqual(ev.data.fieldName, 'fieldName'); assert.strictEqual(ev.data.fieldType, 'boolean'); assert.strictEqual(ev.data.type, 'groupBy'); groupByMenu.update([{ description: 'Candlelight', groupNumber: 1, isActive: true, }]); }, }, } ); testUtils.dom.click(groupByMenu.$('button:first')); testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); assert.strictEqual(groupByMenu.$('select').val(), 'fieldName', 'the select value should be "fieldName"'); testUtils.dom.click(groupByMenu.$('button.o_apply_group')); assert.containsOnce(groupByMenu, '.o_menu_item > .dropdown-item.selected', 'there should be a groupby selected'); groupByMenu.destroy(); });
QUnit.test('delegate', async function (assert) { assert.expect(5); var a = []; var widget = new (Widget.extend({ template: 'test.widget.template', events: { 'click': function () { a[0] = true; assert.strictEqual(this, widget, "should trigger events in widget"); }, 'click li.class-3': 'class3', 'change input': function () { a[2] = true; } }, class3: function () { a[1] = true; } }))(); widget.renderElement(); await testUtils.dom.click(widget.$el, {allowInvisible: true}); await testUtils.dom.click(widget.$('li:eq(3)'), {allowInvisible: true}); await testUtils.fields.editAndTrigger(widget.$('input:last'), 'foo', 'change'); for(var i=0; i<3; ++i) { assert.ok(a[i], "should pass test " + i); } widget.destroy(); });
QUnit.test('undelegate', async function (assert) { assert.expect(4); var clicked = false; var newclicked = false; var widget = new (Widget.extend({ template: 'test.widget.template', events: { 'click li': function () { clicked = true; } } }))(); widget.renderElement(); widget.$el.on('click', 'li', function () { newclicked = true; }); await testUtils.dom.clickFirst(widget.$('li'), {allowInvisible: true}); assert.ok(clicked, "should trigger bound events"); assert.ok(newclicked, "should trigger bound events"); clicked = newclicked = false; widget._undelegateEvents(); await testUtils.dom.clickFirst(widget.$('li'), {allowInvisible: true}); assert.ok(!clicked, "undelegate should unbind events delegated"); assert.ok(newclicked, "undelegate should only unbind events it created"); widget.destroy(); });
QUnit.test('thread window\'s input can still be focused when the UI is blocked', function (assert) { assert.expect(2); var parent = this.createParent({ data: this.data, services: this.services, }); var $dom = $('#qunit-fixture'); // get channel instance to link to thread window var channel = parent.call('mail_service', 'getChannel', 1); channel.detach(); var $input = $('<input/>', {type: 'text'}).appendTo($dom); testUtils.dom.click($input.focus()); assert.strictEqual(document.activeElement, $input[0], "fake input should be focused"); framework.blockUI(); // cannot force focus here otherwise the test // makes no sense, this test is just about // making sure that the code which forces the // focus on click is not removed testUtils.dom.click($('.o_thread_window .o_composer_text_field')); assert.strictEqual(document.activeElement, $('.o_thread_window .o_composer_text_field')[0], "thread window's input should now be focused"); framework.unblockUI(); parent.destroy(); });
QUnit.test("building a domain with a datetime", function (assert) { assert.expect(2); var $target = $("#qunit-fixture"); // Create the domain selector and its mock environment var domainSelector = new DomainSelector(null, "partner", [["nice_datetime", "=", "2017-03-27 15:42:00"]], { readonly: false, }); testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); domainSelector.appendTo($target); // Check that there is a datepicker to choose the date var $datepicker = domainSelector.$(".o_datepicker:visible"); assert.strictEqual($datepicker.length, 1, "there should be a datepicker"); var val = $datepicker.find('input').val(); testUtils.dom.openDatepicker($datepicker); testUtils.dom.clickFirst($('.bootstrap-datetimepicker-widget :not(.today)[data-action="selectDay"]')); assert.notEqual(domainSelector.$(".o_datepicker:visible input").val(), val, "datepicker value should have changed"); domainSelector.destroy(); });
QUnit.test('adding a simple filter works', function (assert) { assert.expect(6); delete this.fields.date_field; var filterMenu = createFilterMenu([], this.fields, { intercepts: { new_filters: function (ev) { var filter = ev.data.filters[0]; assert.strictEqual(filter.type, 'filter'); assert.strictEqual(filter.description, 'Boolean Field is true'); assert.strictEqual(filter.domain, '[[\"boolean_field\",\"=\",True]]'); filterMenu.update([{ isActive: true, description: '?', domain: '?', groupNumber: 1, }]); }, }, }); // open menu dropdown and custom filter submenu, remove existing prop testUtils.dom.click(filterMenu.$('span.fa-filter')); testUtils.dom.click(filterMenu.$('.o_add_custom_filter')); // click on apply to activate filter testUtils.dom.click(filterMenu.$('.o_apply_filter')); assert.containsNone(filterMenu, '.o_filter_condition'); assert.containsN(filterMenu, '.dropdown-divider', 2); assert.isNotVisible(filterMenu.$('.dropdown-divider').eq(0)); filterMenu.destroy(); });
QUnit.test('SelectCreateDialog: save current search', function (assert) { assert.expect(4); testUtils.mock.patch(ListController, { getContext: function () { return { shouldBeInFilterContext: true, }; }, }); var parent = createParent({ data: this.data, archs: { 'partner,false,list': '<tree>' + '<field name="display_name"/>' + '</tree>', 'partner,false,search': '<search>' + '<filter name="bar" help="Bar" domain="[(\'bar\', \'=\', True)]"/>' + '</search>', }, intercepts: { create_filter: function (event) { var filter = event.data.filter; assert.deepEqual(filter.domain, "[('bar', '=', True)]", "should save the correct domain"); assert.deepEqual(filter.context, {shouldBeInFilterContext: true}, "should save the correct context"); }, }, }); var dialog = new dialogs.SelectCreateDialog(parent, { context: {shouldNotBeInFilterContext: false}, res_model: 'partner', }).open(); assert.containsN(dialog, '.o_data_row', 3, "should contain 3 records"); // filter on bar testUtils.dom.click(dialog.$('.o_dropdown_toggler_btn:contains(Filters)')); testUtils.dom.click(dialog.$('.o_filters_menu a:contains(Bar)')); assert.containsN(dialog, '.o_data_row', 2, "should contain 2 records"); // save filter testUtils.dom.click(dialog.$('.o_dropdown_toggler_btn:contains(Favorites)')); testUtils.dom.click(dialog.$('.o_save_search')); dialog.$('.o_save_name input[type=text]').val('some name'); // name the filter testUtils.dom.click(dialog.$('.o_save_name button')); testUtils.mock.unpatch(ListController); parent.destroy(); });
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", async function ( assert ) { assert.expect(8); testUtils.mock.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.mock.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, }); await messagingMenu.appendTo($('#qunit-fixture')); await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.hasClass(messagingMenu.$el,'o_mail_systray_item', 'should be the instance of widget'); assert.hasClass(messagingMenu.$el,'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.containsOnce(messagingMenu, '.o_mail_preview_mark_as_read', "should have mark as read icon next to preview"); await testUtils.dom.click(messagingMenu.$(".o_mail_preview_mark_as_read")); assert.verifySteps(['markedAsRead'], "the document thread should be marked as read"); testUtils.mock.unpatch(DocumentThread); messagingMenu.destroy(); });
QUnit.test('select a groupBy of date type in Add Custom Group menu add properly that groupBy to menu', function (assert) { assert.expect(13); INTERVAL_OPTIONS = INTERVAL_OPTIONS.map(function (option) { return _.extend(option, {description: option.description.toString()}); }); var groupByMenu = createGroupByMenu( [], this.fields, { intercepts: { new_groupBy: function (ev) { assert.strictEqual(ev.data.description, 'Super Date'); assert.strictEqual(ev.data.fieldName, 'fieldName'); assert.strictEqual(ev.data.fieldType, 'date'); assert.strictEqual(ev.data.type, 'groupBy'); assert.strictEqual(ev.data.hasOptions, true); assert.deepEqual(ev.data.options, controlPanelViewParameters.INTERVAL_OPTIONS); assert.strictEqual(ev.data.defaultOptionId, controlPanelViewParameters.DEFAULT_INTERVAL); assert.strictEqual(ev.data.currentOptionId, false); groupByMenu.update([{ description: 'Super Date', fieldName: 'fieldName', groupNumber: 1, isActive: true, hasOptions: true, options: controlPanelViewParameters.INTERVAL_OPTIONS, currentOptionId: controlPanelViewParameters.DEFAULT_INTERVAL, }]); }, }, } ); // open groupBy menu testUtils.dom.click(groupByMenu.$('button:first')); // open Add Custom Group submenu testUtils.dom.click(groupByMenu.$('.o_add_custom_group')); // select fieldName assert.strictEqual(groupByMenu.$('select').val(), 'fieldName', 'the select value should be "fieldName"'); // create new groupBy of type date groupByMenu.$('button.o_apply_group').click(); assert.strictEqual(groupByMenu.$('.o_menu_item > .dropdown-item.selected').length, 1, 'there should be a groupby selected'); assert.strictEqual(groupByMenu.$('.o_menu_item .o_submenu_switcher').length, 1, 'there should be options available'); // open options submenu groupByMenu.$('.o_menu_item .o_submenu_switcher').click(); assert.strictEqual(groupByMenu.$('.o_item_option').length, 5, 'there should be five options available'); assert.strictEqual(groupByMenu.$('.o_add_custom_group').length, 0, 'there should be no more a Add Custome Group submenu'); groupByMenu.destroy(); });
return concurrency.delay(0).then(function () { assert.ok(graph.$('text.nv-legend-text:contains(Count)').length, "should have used the correct measure"); assert.ok(graph.$buttons.find('.dropdown-item[data-field="foo"]').length, "should have foo in the list of measures"); testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)')); testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="foo"]')); return concurrency.delay(0); }).then(function () {
QUnit.test('settings views does not read existing id when coming back in breadcrumbs', function (assert) { assert.expect(8); var actions = [{ id: 1, name: 'Settings view', res_model: 'project', type: 'ir.actions.act_window', views: [[1, 'form']], }, { id: 4, name: 'Other action', res_model: 'project', type: 'ir.actions.act_window', views: [[2, 'list']], }]; var archs = { 'project,1,form': '<form string="Settings" js_class="base_settings">' + '<div class="app_settings_block" string="CRM" data-key="crm">' + '<button name="4" string="Execute action" type="action"/>' + '</div>' + '</form>', 'project,2,list': '<tree><field name="foo"/></tree>', 'project,false,search': '<search></search>', }; var actionManager = createActionManager({ actions: actions, archs: archs, data: this.data, mockRPC: function (route, args) { if (args.method) { assert.step(args.method); } return this._super.apply(this, arguments); }, }); actionManager.doAction(1); testUtils.dom.click(actionManager.$('button[name="4"]')); testUtils.dom.click($('.o_control_panel .breadcrumb-item a')); assert.hasClass(actionManager.$('.o_form_view'), 'o_form_editable'); assert.verifySteps([ 'load_views', // initial setting action 'default_get', // this is a setting view => create new record 'create', // when we click on action button => save 'read', // with save, we have a reload... (not necessary actually) 'load_views', // for other action in breadcrumb, // with a searchread (not shown here since it is a route) 'default_get', // when we come back, we want to restart from scratch ]); actionManager.destroy(); });
QUnit.test('No Price Available', async function (assert) { assert.expect(11); this.data['mail.message'].records[0].snailmail_status = 'no_price_available'; this.data['mail.message'].records[0].snailmail_error = true; var form = await createView({ View: FormView, model: 'account.invoice', res_id: 1, data: this.data, services: this.services, arch: getArch(), mockRPC: function (route, args) { if (args.method === 'cancel_letter' && args.model === 'mail.message' && args.args[0][0] === 11) { assert.step(args.method); return Promise.resolve(); } return this._super.apply(this, arguments); } }); assert.containsOnce(form, '.o_thread_snailmail_tooltip_container', "Snailmail icon should appear on message"); assert.containsOnce(form, '.o_thread_message_snailmail_no_price_available', "Snailmail status of message should be 'no_price_available'"); assert.containsNone(form, '.o_thread_tooltip_snailmail', "No tooltip should be present"); testUtils.dom.triggerMouseEvent(form.$('.o_thread_message_snailmail_no_price_available'), 'mouseenter'); assert.ok($('.o_thread_tooltip_snailmail:visible').length, "Tooltip should appear when hovering the Snailmail Icon"); assert.ok($('.o_thread_tooltip_snailmail_icon.fa-exclamation').length, "Tooltip should show correct icon"); assert.ok($('.o_thread_tooltip_snailmail:contains("Error")').length, "Tooltip should show correct text"); await testUtils.dom.click(form.$('.o_thread_message_snailmail_no_price_available')); var $modal = $('.modal'); assert.ok($modal.length, "A modal should open on click"); assert.containsOnce($modal, 'button:contains("Cancel letter")', "Modal should have a 'Cancel letter' button"); var $cancelButton = $('.modal').find('button:contains("Cancel letter")'); await testUtils.dom.click($cancelButton); assert.notOk($('.modal').length, "The modal should be closed after click on 'Cancel letter'"); assert.verifySteps(['cancel_letter'], "Should have made a RPC call to 'cancel_letter'"); form.destroy(); });
QUnit.test('fold Support channel', function (assert) { assert.expect(11); var messagingMenu = new MessagingMenu(); addMockSupportEnvironment(messagingMenu, { data: this.data, mockRPC: function (route, args) { if (!_.string.endsWith(route, '.png')) { // ignore images assert.step(args.method || route); } return this._super.apply(this, arguments); }, mockSupportRPC: function (route) { if (route.split('/')[1] === 'odoo_im_support') { assert.step('cors: ' + route); } return this._super.apply(this, arguments); }, services: this.services, session: this.supportParams, }); 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')); 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]')); 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')); testUtils.dom.click($('.o_thread_window .o_thread_window_close')); assert.verifySteps([ '/mail/init_messaging', 'message_fetch', 'cors: /odoo_im_support/get_support_channel', 'LocalStorage: setItem im_support.channel_state,open', 'cors: /odoo_im_support/fetch_messages', 'LocalStorage: setItem im_support.channel_state,folded', 'LocalStorage: setItem im_support.channel_state,open', 'LocalStorage: setItem im_support.channel_state,closed', ]); messagingMenu.destroy(); });
QUnit.test('click on an option should toggle options and item states properly', function (assert) { assert.expect(22); this.items[0].options = [{optionId: 1, description: "First Option"}, {optionId: 2, description: "Second Option"}]; var eventNumber = 0; var dropdownMenu = createDropdownMenu(this.dropdownHeader, this.items, { intercepts: { menu_item_toggled: function (ev) { eventNumber++; if (eventNumber === 1) { assert.strictEqual(ev.data.itemId, 1); assert.strictEqual(ev.data.isActive, true); assert.strictEqual(ev.data.optionId, 1); } else { assert.strictEqual(ev.data.itemId, 1); assert.strictEqual(ev.data.isActive, false); assert.strictEqual(ev.data.optionId, false); } }, item_option_changed: function (ev) { if (eventNumber === 1) { assert.strictEqual(ev.data.itemId, 1); assert.strictEqual(ev.data.isActive, true); assert.strictEqual(ev.data.optionId, 2); } }, }, }); // open dropdown menu testUtils.dom.click(dropdownMenu.$('button:first')); // open options menu testUtils.dom.click(dropdownMenu.$('span.fa-caret-right')); assert.containsN(dropdownMenu, '.dropdown-divider, .dropdown-item, .dropdown-item-text', 7); // Don't forget there is a hidden li.divider element at first place among children assert.doesNotHaveClass(dropdownMenu.$('.o_menu_item:nth-child(2) > .dropdown-item'), 'selected'); assert.doesNotHaveClass(dropdownMenu.$('.o_item_option:nth-child(2) > .dropdown-item'), 'selected'); assert.doesNotHaveClass(dropdownMenu.$('.o_item_option:nth-child(3) > .dropdown-item'), 'selected'); testUtils.dom.click(dropdownMenu.$('.o_item_option:first')); assert.hasClass(dropdownMenu.$('.o_menu_item:nth-child(2) > .dropdown-item'), 'selected'); assert.hasClass(dropdownMenu.$('.o_item_option:nth-child(2) > .dropdown-item'), 'selected'); assert.doesNotHaveClass(dropdownMenu.$('.o_item_option:nth-child(3) > .dropdown-item'), 'selected'); testUtils.dom.click(dropdownMenu.$('.o_item_option:nth-child(3)')); assert.hasClass(dropdownMenu.$('.o_menu_item:nth-child(2) > .dropdown-item'), 'selected'); assert.doesNotHaveClass(dropdownMenu.$('.o_item_option:nth-child(2) > .dropdown-item'), 'selected'); assert.hasClass(dropdownMenu.$('.o_item_option:nth-child(3) > .dropdown-item'), 'selected'); testUtils.dom.click(dropdownMenu.$('.o_item_option:nth-child(3)')); assert.doesNotHaveClass(dropdownMenu.$('.o_menu_item:nth-child(2) > .dropdown-item'), 'selected'); assert.doesNotHaveClass(dropdownMenu.$('.o_item_option:nth-child(2) > .dropdown-item'), 'selected'); assert.doesNotHaveClass(dropdownMenu.$('.o_item_option:nth-child(3) > .dropdown-item'), 'selected'); dropdownMenu.destroy(); });
QUnit.test('mobile grouped rendering', function (assert) { assert.expect(11); var kanban = createView({ View: KanbanView, model: 'partner', data: this.data, arch: '<kanban class="o_kanban_test o_kanban_small_column" on_create="quick_create">' + '<templates><t t-name="kanban-box">' + '<div><field name="foo"/></div>' + '</t></templates>' + '</kanban>', domain: [['product_id', '!=', false]], groupBy: ['product_id'], }); // basic rendering tests assert.containsN(kanban, '.o_kanban_group', 2, "should have 2 columns" ); assert.hasClass(kanban.$('.o_kanban_mobile_tab:first'),'o_current', "first tab is the active tab with class 'o_current'"); assert.hasClass(kanban.$('.o_kanban_group:first'),'o_current', "first column is the active column with class 'o_current'"); assert.containsN(kanban, '.o_kanban_group:first > div.o_kanban_record', 2, "there are 2 records in active tab"); assert.strictEqual(kanban.$('.o_kanban_group:nth(1) > div.o_kanban_record').length, 0, "there is no records in next tab. Records will be loaded when it will be opened"); // quick create in first column testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); assert.hasClass(kanban.$('.o_kanban_group:nth(0) > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the first column"); // move to second column kanban.$('.o_kanban_mobile_tab:nth(1)').trigger('click'); assert.hasClass(kanban.$('.o_kanban_mobile_tab:nth(1)'),'o_current', "second tab is now active with class 'o_current'"); assert.hasClass(kanban.$('.o_kanban_group:nth(1)'),'o_current', "second column is now active with class 'o_current'"); assert.strictEqual(kanban.$('.o_kanban_group:nth(1) > div.o_kanban_record').length, 2, "the 2 records of the second group have now been loaded"); // quick create in second column testUtils.dom.click(kanban.$buttons.find('.o-kanban-button-new')); assert.hasClass(kanban.$('.o_kanban_group:nth(1) > div:nth(1)'),'o_kanban_quick_create', "clicking on create should open the quick_create in the second column"); // kanban column should match kanban mobile tabs var column_ids = kanban.$('.o_kanban_group').map(function(){ return $(this).data('id') }).get(); var tab_ids = kanban.$('.o_kanban_mobile_tab').map(function(){ return $(this).data('id') }).get(); assert.deepEqual(column_ids, tab_ids, "all columns data-id should match mobile tabs data-id"); kanban.destroy(); });
QUnit.test('activity menu widget: activity view icon', function (assert) { assert.expect(10); var self = this; var activityMenu = new ActivityMenu(); testUtils.mock.addMockEnvironment(activityMenu, { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { return $.when(self.data['mail.activity.menu'].records); } return this._super(route, args); }, }); activityMenu.appendTo($('#qunit-fixture')); assert.containsN(activityMenu, '.o_mail_activity_action', 2, "widget should have 2 activity view icons"); var $first = activityMenu.$('.o_mail_activity_action').eq(0); var $second = activityMenu.$('.o_mail_activity_action').eq(1); assert.strictEqual($first.data('model_name'), "Issue", "first activity action should link to 'Issue'"); assert.hasClass($first,'fa-clock-o', "should display the activity action icon"); assert.strictEqual($second.data('model_name'), "Note", "Second activity action should link to 'Note'"); assert.hasClass($second,'fa-clock-o', "should display the activity action icon"); testUtils.mock.intercept(activityMenu, 'do_action', function (ev) { if (ev.data.action.name) { assert.ok(ev.data.action.context, "should define a context on the action"); assert.ok(ev.data.action.context.search_default_activities_my, "should auto-set search to 'My Activities' in the context of the action"); assert.step('do_action:' + ev.data.action.name); } else { assert.step('do_action:' + ev.data.action); } }, true); // click on the "Issue" activity icon testUtils.dom.click(activityMenu.$('.dropdown-toggle')); testUtils.dom.click(activityMenu.$(".o_mail_activity_action[data-model_name='Issue']")); // click on the "Note" activity icon testUtils.dom.click(activityMenu.$(".o_mail_activity_action[data-model_name='Note']")); assert.verifySteps([ 'do_action:Issue', 'do_action:mail.mail_activity_type_view_tree' ]); 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('click on an item should not change url', function (assert) { assert.expect(0); var dropdownMenu = createDropdownMenu(this.dropdownHeader, this.items); testUtils.dom.click(dropdownMenu.$('.o_dropdown_toggler_btn')); dropdownMenu.$el.click(function () { // we do not want a click to get out and change the url, for example throw new Error('No click should get out of the dropdown menu'); }); testUtils.dom.click(dropdownMenu.$('.o_menu_item a').first()); dropdownMenu.destroy(); });
QUnit.test('Display a question', function (assert) { var done = assert.async(); assert.expect(8); var view = createView(this.viewParams); function notification (inc) { return { title: 'a' + inc, message: 'b' + inc, buttons: [ { text: 'accept' + inc, primary: true, click: function () { assert.step('accept' + inc); }, }, { text: 'refuse' + inc, click: function () { assert.step('refuse' + inc); }, } ], onClose: function () { assert.step('close' + inc); } }; }; view.call('notification', 'notify', notification(0)); view.call('notification', 'notify', notification(1)); view.call('notification', 'notify', notification(2)); var $notification = $('body .o_notification_manager .o_notification'); assert.strictEqual($notification.eq(0).find('.o_close').length, 1, "should display the close button in notification"); assert.strictEqual(_.str.trim($notification.eq(0).html().replace(/\s+/g, ' ')), "<a aria-label=\"Close\" class=\"fa fa-times o_close\" href=\"#\" title=\"Close\"></a> <div class=\"o_notification_title\"> <span role=\"img\" aria-label=\"Notification undefined\" class=\"o_icon fa fa-3x fa-question-circle-o\" title=\"Notification undefined\"></span> a0 </div> <div class=\"o_notification_content\">b0</div> <div class=\"o_buttons\"> <button class=\"btn btn-primary\" type=\"button\"> <span>accept0</span> </button><button class=\"btn btn-secondary\" type=\"button\"> <span>refuse0</span> </button> </div>", "should display notification"); testUtils.dom.click($notification.find('.o_buttons button:contains(accept0)')); testUtils.dom.click($notification.find('.o_buttons button:contains(refuse1)')); testUtils.dom.click($notification.eq(2).find('.o_close')); setTimeout(function () { assert.strictEqual($notification.is(':hidden'), true, "should hide the notification"); assert.strictEqual($('body .o_notification_manager .o_notification').length, 0, "should destroy the notification"); assert.verifySteps(['accept0', 'refuse1', 'close2']); view.destroy(); done(); }); });
QUnit.test('messaging menu widget: open inbox for needaction not linked to any document', async function (assert) { assert.expect(4); var messagingMenu = new MessagingMenu(); testUtils.mock.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, session: { partner_id: 1, }, }); await 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 await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); 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"); testUtils.mock.intercept(messagingMenu, 'do_action', function (ev) { if (ev.data.action === 'mail.action_discuss') { assert.step('do_action:' + ev.data.action + ':' + ev.data.options.active_id); } }, true); await testUtils.dom.click($firstChannelPreview); assert.verifySteps( ['do_action:mail.action_discuss:mailbox_inbox'], "should open Discuss with Inbox"); messagingMenu.destroy(); });
QUnit.test('save to dashboard actions with flag keepSearchView', function (assert) { assert.expect(4); var actionManager = createActionManager({ data: this.data, archs: { 'partner,false,graph': '<graph><field name="foo"/></graph>', 'partner,false,list': '<list><field name="foo"/></list>', 'partner,false,search': '<search></search>', }, mockRPC: function (route, args) { if (route === '/board/add_to_dashboard') { assert.strictEqual(args.action_id, 2, "should save the correct action"); assert.strictEqual(args.view_mode, 'graph', "should save the correct view type"); return $.when(true); } return this._super.apply(this, arguments); }, }); // execute a first action actionManager.doAction({ id: 1, res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'list']], }); // execute another action with flag 'keepSearchView' and add it to dashboard var options = {keepSearchView: true}; actionManager.doAction({ id: 2, res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'graph']], }, options); assert.containsOnce(actionManager, '.o_graph', "should display the graph view"); assert.strictEqual($('.o_add_to_dashboard_link').length, 1, "should allow the 'Add to dashboard' feature (this is the same searchview)"); // add this action to dashboard testUtils.dom.click($('.o_search_options .o_dropdown button:contains(Favorites)')); testUtils.dom.click($('.o_add_to_dashboard_link')); testUtils.dom.click($('.o_add_to_dashboard_button')); actionManager.destroy(); });
QUnit.test("form: Manage Attachments option", async function (assert) { assert.expect(3); var debugManager = createDebugManager({ intercepts: { do_action: function (event) { assert.deepEqual(event.data.action, { context: { default_res_model: "test.model", default_res_id: 5, }, domain: [["res_model", "=", "test.model"],["res_id", "=", 5]], name: "Manage Attachments", res_model: "ir.attachment", type: "ir.actions.act_window", views: [[false, "list"],[false, "form"]], }); }, }, }); await debugManager.appendTo($('#qunit-fixture')); // Simulate update debug manager from web client var action = { views: [{ displayName: "Form", fieldsView: { view_id: 2, }, type: "form", }], res_model: "test.model", }; var view = { viewType: "form", getSelectedIds: function () { return [5]; }, }; await debugManager.update('action', action, view); var $attachmentMenu = debugManager.$('a[data-action=get_attachments]'); assert.strictEqual($attachmentMenu.length, 1, "should have Manage Attachments menu item"); assert.strictEqual($attachmentMenu.text().trim(), "Manage Attachments", "should have correct menu item text"); await testUtils.dom.click(debugManager.$('> a')); // open dropdown await testUtils.dom.click($attachmentMenu); debugManager.destroy(); });
return concurrency.delay(0).then(function () { // need to set the measure this way because it cannot be set in the // arch. testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)')); testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="product_id"]')); assert.strictEqual(graph.model.chart.data[0].value, 1, "should have first datapoint with value 1"); assert.strictEqual(graph.model.chart.data[1].value, 1, "should have second datapoint with value 1"); graph.destroy(); done(); });
QUnit.test('Display a question', async function (assert) { assert.expect(8); var view = await createView(this.viewParams); function notification (inc) { return { title: 'a' + inc, message: 'b' + inc, buttons: [ { text: 'accept' + inc, primary: true, click: function () { assert.step('accept' + inc); }, }, { text: 'refuse' + inc, click: function () { assert.step('refuse' + inc); }, } ], onClose: function () { assert.step('close' + inc); } }; }; view.call('notification', 'notify', notification(0)); view.call('notification', 'notify', notification(1)); view.call('notification', 'notify', notification(2)); await testUtils.nextTick(); var $notification = $('body .o_notification_manager .o_notification'); assert.containsOnce($notification.eq(0), '.o_notification_close', "should display the close button in notification"); assert.strictEqual(_.str.trim($notification.eq(0).html().replace(/\s+/g, ' ')), "<div class=\"toast-header\"> <span role=\"img\" aria-label=\"Notification undefined\" class=\"fa fa-2x mr-3 fa-question-circle-o o_notification_icon\" title=\"Notification undefined\"></span> <div class=\"d-flex align-items-center mr-auto font-weight-bold o_notification_title\">a0</div> <button aria-label=\"Close\" class=\"mb-1 close o_notification_close\" data-dismiss=\"toast\" type=\"button\"> <span aria-hidden=\"true\" class=\"d-inline\">×</span> </button> </div> <div class=\"toast-body\"> <div class=\"o_notification_content\">b0</div> <div class=\"mt-2 o_notification_buttons\"> <button class=\"btn btn-primary\" type=\"button\"> <span>accept0</span> </button><button class=\"btn btn-secondary\" type=\"button\"> <span>refuse0</span> </button> </div> </div>", "should display notification"); testUtils.dom.click($notification.find('.o_notification_buttons button:contains(accept0)')); testUtils.dom.click($notification.find('.o_notification_buttons button:contains(refuse1)')); testUtils.dom.click($notification.eq(2).find('.o_notification_close')); assert.strictEqual($notification.is(':hidden'), true, "should hide the notification"); assert.strictEqual($('body .o_notification_manager .o_notification').length, 0, "should destroy the notification"); assert.verifySteps(['accept0', 'refuse1', 'close2']); view.destroy(); });