odoo.define('web.favorite_menu_tests', function (require) { "use strict"; const FormView = require('web.FormView'); const testUtils = require('web.test_utils'); const cpHelpers = testUtils.controlPanel; const { createControlPanel, createView, mock } = testUtils; const { patchDate } = mock; const searchMenuTypes = ['favorite']; QUnit.module('Components', { beforeEach: function () { this.fields = { bar: { string: "Bar", type: "many2one", relation: 'partner' }, birthday: { string: "Birthday", type: "date", store: true, sortable: true }, date_field: { string: "Date", type: "date", store: true, sortable: true }, float_field: { string: "Float", type: "float", group_operator: 'sum' }, foo: { string: "Foo", type: "char", store: true, sortable: true }, }; }, }, function () { QUnit.module('FavoriteMenu'); QUnit.test('simple rendering with no favorite', async function (assert) { assert.expect(8); const params = { cpModelConfig: { searchMenuTypes }, cpProps: { fields: this.fields, searchMenuTypes, action: { name: "Action Name" } }, }; const controlPanel = await createControlPanel(params); assert.containsOnce(controlPanel, 'div.o_favorite_menu > button i.fa.fa-star'); assert.strictEqual(controlPanel.el.querySelector('div.o_favorite_menu > button span').innerText.trim(), "Favorites"); await cpHelpers.toggleFavoriteMenu(controlPanel); assert.containsNone(controlPanel, '.dropdown-divider'); assert.containsOnce(controlPanel, '.o_add_favorite'); assert.strictEqual(controlPanel.el.querySelector('.o_add_favorite > button').innerText.trim(), "Save current search"); await cpHelpers.toggleSaveFavorite(controlPanel); assert.strictEqual( controlPanel.el.querySelector('.o_add_favorite input[type="text"]').value, 'Action Name' ); assert.containsN(controlPanel, '.o_add_favorite .custom-checkbox input[type="checkbox"]', 2); const labelEls = controlPanel.el.querySelectorAll('.o_add_favorite .custom-checkbox label'); assert.deepEqual( [...labelEls].map(e => e.innerText.trim()), ["Use by default", "Share with all users"] ); controlPanel.destroy(); }); QUnit.test('favorites use by default and share are exclusive', async function (assert) { assert.expect(11); const params = { cpModelConfig: { viewInfo: { fields: this.fields }, searchMenuTypes }, cpProps: { fields: this.fields, searchMenuTypes, action: {}, }, }; const controlPanel = await createControlPanel(params); await cpHelpers.toggleFavoriteMenu(controlPanel); await cpHelpers.toggleSaveFavorite(controlPanel); const checkboxes = controlPanel.el.querySelectorAll('input[type="checkbox"]'); assert.strictEqual(checkboxes.length, 2, '2 checkboxes are present'); assert.notOk(checkboxes[0].checked, 'Start: None of the checkboxes are checked (1)'); assert.notOk(checkboxes[1].checked, 'Start: None of the checkboxes are checked (2)'); await testUtils.dom.click(checkboxes[0]); assert.ok(checkboxes[0].checked, 'The first checkbox is checked'); assert.notOk(checkboxes[1].checked, 'The second checkbox is not checked'); await testUtils.dom.click(checkboxes[1]); assert.notOk(checkboxes[0].checked, 'Clicking on the second checkbox checks it, and unchecks the first (1)'); assert.ok(checkboxes[1].checked, 'Clicking on the second checkbox checks it, and unchecks the first (2)'); await testUtils.dom.click(checkboxes[0]); assert.ok(checkboxes[0].checked, 'Clicking on the first checkbox checks it, and unchecks the second (1)'); assert.notOk(checkboxes[1].checked, 'Clicking on the first checkbox checks it, and unchecks the second (2)'); await testUtils.dom.click(checkboxes[0]); assert.notOk(checkboxes[0].checked, 'End: None of the checkboxes are checked (1)'); assert.notOk(checkboxes[1].checked, 'End: None of the checkboxes are checked (2)'); controlPanel.destroy(); }); QUnit.test('save filter', async function (assert) { assert.expect(1); const params = { cpModelConfig: { fields: this.fields, searchMenuTypes }, cpProps: { fields: this.fields, searchMenuTypes, action: {}, }, 'get-controller-query-params': function (callback) { callback({ orderedBy: [ { asc: true, name: 'foo' }, { asc: false, name: 'bar' } ] }); }, env: { dataManager: { create_filter: async function (filter) { assert.strictEqual(filter.sort, '["foo","bar desc"]', 'The right format for the string "sort" should be sent to the server' ); } } }, }; const controlPanel = await createControlPanel(params); await cpHelpers.toggleFavoriteMenu(controlPanel); await cpHelpers.toggleSaveFavorite(controlPanel); await cpHelpers.editFavoriteName(controlPanel, "aaa"); await cpHelpers.saveFavorite(controlPanel); controlPanel.destroy(); }); QUnit.test('dynamic filters are saved dynamic', async function (assert) { assert.expect(3); const arch = ` `; const params = { cpModelConfig: { fields: {}, arch , searchMenuTypes, context: { search_default_positive: true, } }, cpProps: { fields: {}, searchMenuTypes, action: {}, }, 'get-controller-query-params': function (callback) { callback(); }, env: { dataManager: { create_filter: async function (filter) { assert.strictEqual( filter.domain, "[(\"date_field\", \">=\", (context_today() + relativedelta()).strftime(\"%Y-%m-%d\"))]" ); return 1; // serverSideId } } }, }; const controlPanel = await createControlPanel(params); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), ['Float']); await cpHelpers.toggleFavoriteMenu(controlPanel); await cpHelpers.toggleSaveFavorite(controlPanel); await cpHelpers.editFavoriteName(controlPanel, "My favorite"); await cpHelpers.saveFavorite(controlPanel); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), ["My favorite"]); controlPanel.destroy(); }); QUnit.test('save filters created via autocompletion works', async function (assert) { assert.expect(4); const arch = ``; const params = { cpModelConfig: { fields: this.fields, arch , searchMenuTypes, }, cpProps: { fields: this.fields, searchMenuTypes, action: {}, }, 'get-controller-query-params': function (callback) { callback(); }, env: { dataManager: { create_filter: async function (filter) { assert.strictEqual( filter.domain, `[["foo", "ilike", "a"]]` ); return 1; // serverSideId } } }, }; const controlPanel = await createControlPanel(params); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), []); await cpHelpers.editSearch(controlPanel, "a"); await cpHelpers.validateSearch(controlPanel); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), ["Foo\na"]); await cpHelpers.toggleFavoriteMenu(controlPanel); await cpHelpers.toggleSaveFavorite(controlPanel); await cpHelpers.editFavoriteName(controlPanel, "My favorite"); await cpHelpers.saveFavorite(controlPanel); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), ["My favorite"]); controlPanel.destroy(); }); QUnit.test('delete an active favorite remove it both in list of favorite and in search bar', async function (assert) { assert.expect(6); const favoriteFilters = [{ context: "{}", domain: "[['foo', '=', 'qsdf']]", id: 7, is_default: true, name: "My favorite", sort: "[]", user_id: [2, "Mitchell Admin"], }]; const params = { cpModelConfig: { favoriteFilters, searchMenuTypes }, cpProps: { searchMenuTypes, action: {} }, search: function (searchQuery) { const { domain } = searchQuery; assert.deepEqual(domain, []); }, env: { dataManager: { delete_filter: function () { return Promise.resolve(); } } }, }; const controlPanel = await createControlPanel(params); await cpHelpers.toggleFavoriteMenu(controlPanel); const { domain } = controlPanel.getQuery(); assert.deepEqual(domain, [["foo", "=", "qsdf"]]); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), ["My favorite"]); assert.hasClass(controlPanel.el.querySelector('.o_favorite_menu .o_menu_item > a'), 'selected'); await cpHelpers.deleteFavorite(controlPanel, 0); // confirm deletion await testUtils.dom.click(document.querySelector('div.o_dialog footer button')); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), []); const itemEls = controlPanel.el.querySelectorAll('.o_favorite_menu .o_menu_item'); assert.deepEqual([...itemEls].map(e => e.innerText.trim()), ["Save current search"]); controlPanel.destroy(); }); QUnit.test('default favorite is not activated if key search_disable_custom_filters is set to true', async function (assert) { assert.expect(2); const favoriteFilters = [{ context: "{}", domain: "", id: 7, is_default: true, name: "My favorite", sort: "[]", user_id: [2, "Mitchell Admin"], }]; const params = { cpModelConfig: { favoriteFilters, searchMenuTypes, context: { search_disable_custom_filters: true } }, cpProps: { searchMenuTypes, action: {} }, }; const controlPanel = await createControlPanel(params); await cpHelpers.toggleFavoriteMenu(controlPanel); const { domain } = controlPanel.getQuery(); assert.deepEqual(domain, []); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), []); controlPanel.destroy(); }); QUnit.test('toggle favorite correctly clears filter, groupbys, comparison and field "options"', async function (assert) { assert.expect(11); const unpatchDate = patchDate(2019, 6, 31, 13, 43, 0); const favoriteFilters = [{ context: ` { "group_by": ["foo"], "comparison": { "favorite comparison content": "bla bla..." }, } `, domain: "['!', ['foo', '=', 'qsdf']]", id: 7, is_default: false, name: "My favorite", sort: "[]", user_id: [2, "Mitchell Admin"], }]; let firstSearch = true; const arch = ` `; const searchMenuTypes = ['filter', 'groupBy', 'comparison', 'favorite']; const params = { cpModelConfig: { favoriteFilters, arch, fields: this.fields, searchMenuTypes, context: { search_default_positive: true, search_default_coolName: true, search_default_foo: "a", } }, cpProps: { searchMenuTypes, action: {}, fields: this.fields }, search: function (searchQuery) { const { domain, groupBy, timeRanges } = searchQuery; if (firstSearch) { assert.deepEqual(domain, [['foo', 'ilike', 'a']]); assert.deepEqual(groupBy, ['date_field:month']); assert.deepEqual(timeRanges, { comparisonId: "previous_period", comparisonRange: ["&", ["date_field", ">=", "2018-01-01"], ["date_field", "<=", "2018-12-31"]], comparisonRangeDescription: "2018", fieldDescription: "Date Field Filter", fieldName: "date_field", range: ["&", ["date_field", ">=", "2019-01-01"], ["date_field", "<=", "2019-12-31"]], rangeDescription: "2019", }); firstSearch = false; } else { assert.deepEqual(domain, ['!', ['foo', '=', 'qsdf']]); assert.deepEqual(groupBy, ['foo']); assert.deepEqual(timeRanges, { "favorite comparison content": "bla bla...", range: undefined, comparisonRange: undefined, }); } }, }; const controlPanel = await createControlPanel(params); const { domain, groupBy, timeRanges } = controlPanel.getQuery(); assert.deepEqual(domain, [ "&", ["foo", "ilike", "a"], "&", ["date_field", ">=", "2019-01-01"], ["date_field", "<=", "2019-12-31"] ]); assert.deepEqual(groupBy, ['date_field:month']); assert.deepEqual(timeRanges, {}); assert.deepEqual( cpHelpers.getFacetTexts(controlPanel), [ 'Foo\na', 'Date Field Filter: 2019', 'Date Field Groupby: Month', ] ); // activate a comparison await cpHelpers.toggleComparisonMenu(controlPanel); await cpHelpers.toggleMenuItem(controlPanel, "Date Field Filter: Previous period"); // activate the unique existing favorite await cpHelpers.toggleFavoriteMenu(controlPanel); await cpHelpers.toggleMenuItem(controlPanel, 0); assert.deepEqual( cpHelpers.getFacetTexts(controlPanel), ["My favorite"] ); controlPanel.destroy(); unpatchDate(); }); QUnit.test('favorites have unique descriptions (the submenus of the favorite menu are correctly updated)', async function (assert) { assert.expect(3); const favoriteFilters = [{ context: "{}", domain: "[]", id: 1, is_default: false, name: "My favorite", sort: "[]", user_id: [2, "Mitchell Admin"], }]; const params = { cpModelConfig: { favoriteFilters, searchMenuTypes }, cpProps: { searchMenuTypes, action: {} }, 'get-controller-query-params': function (callback) { callback(); }, env: { session: { uid: 4 }, services: { notification: { notify: function (params) { assert.deepEqual(params, { message: "Filter with same name already exists.", type: "danger" }); }, } }, dataManager: { create_filter: async function (irFilter) { assert.deepEqual(irFilter, { "action_id": undefined, "context": { "group_by": [] }, "domain": "[]", "is_default": false, "model_id": undefined, "name": "My favorite 2", "sort": "[]", "user_id": 4, }); return 2; // serverSideId } } }, }; const controlPanel = await createControlPanel(params); await cpHelpers.toggleFavoriteMenu(controlPanel); await cpHelpers.toggleSaveFavorite(controlPanel); // first try: should fail await cpHelpers.editFavoriteName(controlPanel, "My favorite"); await cpHelpers.saveFavorite(controlPanel); // second try: should succeed await cpHelpers.editFavoriteName(controlPanel, "My favorite 2"); await cpHelpers.saveFavorite(controlPanel); await cpHelpers.toggleSaveFavorite(controlPanel); // third try: should fail await cpHelpers.editFavoriteName(controlPanel, "My favorite 2"); await cpHelpers.saveFavorite(controlPanel); controlPanel.destroy(); }); QUnit.test('save search filter in modal', async function (assert) { assert.expect(5); const data = { partner: { fields: { date_field: { string: "Date", type: "date", store: true, sortable: true, searchable: true }, birthday: { string: "Birthday", type: "date", store: true, sortable: true }, foo: { string: "Foo", type: "char", store: true, sortable: true }, bar: { string: "Bar", type: "many2one", relation: 'partner' }, float_field: { string: "Float", type: "float", group_operator: 'sum' }, }, records: [ { id: 1, display_name: "First record", foo: "yop", bar: 2, date_field: "2017-01-25", birthday: "1983-07-15", float_field: 1 }, { id: 2, display_name: "Second record", foo: "blip", bar: 1, date_field: "2017-01-24", birthday: "1982-06-04", float_field: 2 }, { id: 3, display_name: "Third record", foo: "gnap", bar: 1, date_field: "2017-01-13", birthday: "1985-09-13", float_field: 1.618 }, { id: 4, display_name: "Fourth record", foo: "plop", bar: 2, date_field: "2017-02-25", birthday: "1983-05-05", float_field: -1 }, { id: 5, display_name: "Fifth record", foo: "zoup", bar: 2, date_field: "2016-01-25", birthday: "1800-01-01", float_field: 13 }, { id: 7, display_name: "Partner 6", }, { id: 8, display_name: "Partner 7", }, { id: 9, display_name: "Partner 8", }, { id: 10, display_name: "Partner 9", } ], }, }; const form = await createView({ arch: `
`, archs: { 'partner,false,list': '', 'partner,false,search': '', }, data, model: 'partner', res_id: 1, View: FormView, env: { dataManager: { create_filter(filter) { assert.strictEqual(filter.name, "Awesome Test Customer Filter", "filter name should be correct"); }, } }, }); await testUtils.form.clickEdit(form); await testUtils.fields.many2one.clickOpenDropdown('bar'); await testUtils.fields.many2one.clickItem('bar', 'Search'); assert.containsN(document.body, 'tr.o_data_row', 9, "should display 9 records"); await cpHelpers.toggleFilterMenu('.modal'); await cpHelpers.toggleAddCustomFilter('.modal'); assert.strictEqual(document.querySelector('.o_filter_condition select.o_generator_menu_field').value, 'date_field', "date field should be selected"); await cpHelpers.applyFilter('.modal'); assert.containsNone(document.body, 'tr.o_data_row', "should display 0 records"); // Save this search await cpHelpers.toggleFavoriteMenu('.modal'); await cpHelpers.toggleSaveFavorite('.modal'); const filterNameInput = document.querySelector('.o_add_favorite input[type="text"]'); assert.isVisible(filterNameInput, "should display an input field for the filter name"); await testUtils.fields.editInput(filterNameInput, 'Awesome Test Customer Filter'); await testUtils.dom.click(document.querySelector('.o_add_favorite button.btn-primary')); form.destroy(); }); QUnit.test('modal loads saved search filters', async function (assert) { assert.expect(1); const data = { partner: { fields: { bar: { string: "Bar", type: "many2one", relation: 'partner' }, }, // 10 records so that the Search button shows records: Array.apply(null, Array(10)).map(function(_, i) { return { id: i, display_name: "Record " + i, bar: 1 }; }) }, }; const form = await createView({ arch: `
`, data, model: 'partner', res_id: 1, View: FormView, interceptsPropagate: { load_views: function (ev) { assert.ok(ev.data.options.load_filters, "opening dialog should load the filters"); }, }, }); await testUtils.form.clickEdit(form); await testUtils.fields.many2one.clickOpenDropdown('bar'); await testUtils.fields.many2one.clickItem('bar', 'Search'); form.destroy(); }); }); });