From 9a1de0ef050b09aafe25df211b875b8ba7cb1b51 Mon Sep 17 00:00:00 2001 From: "Nasreddin (bon)" Date: Mon, 8 Mar 2021 12:57:03 +0000 Subject: [PATCH] [FIX] web: compute default locale of Date(Time)Picker at the right time Issue - Install "Accounting" - Change user language to arabic - Go to Customer Invoices list view - Try to filter on field "Created On" Traceback is raised from the DatePicker Component Cause The cause is two fold: There is an issue in moment that behaves abnormally when its internals are not able to extract the month in a locale, and, instead of returning an invalid moment, it returns a valid moment with the month set to January which just happens to be the first month of moment's default locale ref: https://github.com/moment/moment/issues/5600 Secondly, the defaultProps "locale" of the DatePicker was set to moment.locale() at the file's initial loading, that is, before any user locale parameters were loaded The bootstrap datepicker was then set with the wrong locale, and the case fell under moment's issue After this commit, the locale of the DatePicker is always the one of the user, and there is no traceback opw-2460156 closes odoo/odoo#68059 X-original-commit: e2c46bb80e1e312dd34ac554d79813ef35282e62 Signed-off-by: Aaron Bohy (aab) Signed-off-by: bon-odoo Co-authored-by: Lucas Perais --- .../static/src/js/components/datepicker.js | 4 +- .../tests/components/datepicker_tests.js | 94 +++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/components/datepicker.js b/addons/web/static/src/js/components/datepicker.js index a11d51b5021..94ce7ece4fe 100644 --- a/addons/web/static/src/js/components/datepicker.js +++ b/addons/web/static/src/js/components/datepicker.js @@ -178,7 +178,7 @@ odoo.define('web.DatePickerOwl', function (require) { today: 'fa fa-calendar-check-o', up: 'fa fa-chevron-up', }, - locale: moment.locale(), + get locale() {return moment.locale();}, maxDate: moment({ y: 9999, M: 11, d: 31 }), minDate: moment({ y: 1000 }), useCurrent: false, @@ -248,7 +248,7 @@ odoo.define('web.DatePickerOwl', function (require) { } } - DateTimePicker.defaultProps = Object.assign({}, DatePicker.defaultProps, { + DateTimePicker.defaultProps = Object.assign(Object.create(DatePicker.defaultProps), { buttons: { showClear: false, showClose: true, diff --git a/addons/web/static/tests/components/datepicker_tests.js b/addons/web/static/tests/components/datepicker_tests.js index 7e59e9620d7..643bcdb5188 100644 --- a/addons/web/static/tests/components/datepicker_tests.js +++ b/addons/web/static/tests/components/datepicker_tests.js @@ -70,6 +70,47 @@ odoo.define('web.datepicker_tests', function (require) { picker.destroy(); }); + QUnit.test("pick a date with locale", async function (assert) { + assert.expect(4); + + // weird shit of moment https://github.com/moment/moment/issues/5600 + // When month regex returns undefined, january is taken (first month of the default "nameless" locale) + const originalLocale = moment.locale(); + // Those parameters will make Moment's internal compute stuff that are relevant to the bug + const months = 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'); + const monthsShort = 'janv._févr._mars_avr._mai_juin_juil._août_custSept._oct._nov._déc.'.split('_'); + moment.defineLocale('frenchForTests', { months, monthsShort, code: 'frTest' , monthsParseExact: true}); + + const hasChanged = testUtils.makeTestPromise(); + const picker = await createComponent(DatePicker, { + translateParameters: { + date_format: "%d %b, %Y", // Those are important too + time_format: "%H:%M:%S", + }, + props: { date: moment('09/01/1997', 'MM/DD/YYYY') }, + intercepts: { + 'datetime-changed': ev => { + assert.step('datetime-changed'); + assert.strictEqual(ev.detail.date.format('MM/DD/YYYY'), '09/02/1997', + "Event should transmit the correct date"); + hasChanged.resolve(); + }, + } + }); + const input = picker.el.querySelector('.o_datepicker_input'); + await testUtils.dom.click(input); + + await testUtils.dom.click(document.querySelectorAll('.datepicker table td')[3]); // next day + + assert.strictEqual(input.value, '02 custSept., 1997'); + assert.verifySteps(['datetime-changed']); + + moment.locale(originalLocale); + moment.updateLocale('englishForTest', null); + + picker.destroy(); + }); + QUnit.test("enter a date value", async function (assert) { assert.expect(5); @@ -194,6 +235,59 @@ odoo.define('web.datepicker_tests', function (require) { picker.destroy(); }); + QUnit.test("pick a date and time with locale", async function (assert) { + assert.expect(5); + + // weird shit of moment https://github.com/moment/moment/issues/5600 + // When month regex returns undefined, january is taken (first month of the default "nameless" locale) + const originalLocale = moment.locale(); + // Those parameters will make Moment's internal compute stuff that are relevant to the bug + const months = 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'); + const monthsShort = 'janv._févr._mars_avr._mai_juin_juil._août_custSept._oct._nov._déc.'.split('_'); + moment.defineLocale('frenchForTests', { months, monthsShort, code: 'frTest' , monthsParseExact: true}); + + const hasChanged = testUtils.makeTestPromise(); + const picker = await createComponent(DateTimePicker, { + translateParameters: { + date_format: "%d %b, %Y", // Those are important too + time_format: "%H:%M:%S", + }, + props: { date: moment('09/01/1997 12:30:01', 'MM/DD/YYYY HH:mm:ss') }, + intercepts: { + 'datetime-changed': ev => { + assert.step('datetime-changed'); + assert.strictEqual(ev.detail.date.format('MM/DD/YYYY HH:mm:ss'), '09/02/1997 15:45:05', + "Event should transmit the correct date"); + hasChanged.resolve(); + }, + } + }); + + const input = picker.el.querySelector('input.o_input.o_datepicker_input'); + + await testUtils.dom.click(input); + await testUtils.dom.click(document.querySelectorAll('.datepicker table td')[3]); // next day + await testUtils.dom.click(document.querySelector('a[title="Select Time"]')); + await testUtils.dom.click(document.querySelector('.timepicker .timepicker-hour')); + await testUtils.dom.click(document.querySelectorAll('.timepicker .hour')[15]); // 15h + await testUtils.dom.click(document.querySelector('.timepicker .timepicker-minute')); + await testUtils.dom.click(document.querySelectorAll('.timepicker .minute')[9]); // 45m + await testUtils.dom.click(document.querySelector('.timepicker .timepicker-second')); + + assert.verifySteps([]); + await testUtils.dom.click(document.querySelectorAll('.timepicker .second')[1]); // 05s + + assert.strictEqual(input.value, '02 custSept., 1997 15:45:05'); + assert.verifySteps(['datetime-changed']); + + await hasChanged; + + moment.locale(originalLocale); + moment.updateLocale('frenchForTests', null); + + picker.destroy(); + }); + QUnit.test("enter a datetime value", async function (assert) { assert.expect(9);