import { expect, test } from "@odoo/hoot"; import { click, edit, queryAll, queryAllTexts, select } from "@odoo/hoot-dom"; import { animationFrame, mockTimeZone } from "@odoo/hoot-mock"; import { clickSave, defineModels, defineParams, fields, models, mountView, onRpc, } from "@web/../tests/web_test_helpers"; import { getPickerApplyButton, getPickerCell, getTimePickers, zoomOut, } from "@web/../tests/core/datetime/datetime_test_helpers"; class Partner extends models.Model { date = fields.Date({ string: "A date", searchable: true }); datetime = fields.Datetime({ string: "A datetime", searchable: true }); p = fields.One2many({ string: "one2many field", relation: "partner", searchable: true, }); _records = [ { id: 1, date: "2017-02-03", datetime: "2017-02-08 10:00:00", p: [], }, { id: 2, date: false, datetime: false, }, ]; } defineModels([Partner]); test("DatetimeField in form view", async () => { mockTimeZone(+2); // UTC+2 await mountView({ type: "form", resModel: "partner", resId: 1, arch: '
', }); const expectedDateString = "02/08/2017 12:00:00"; // 10:00:00 without timezone expect(".o_field_datetime input").toHaveValue(expectedDateString, { message: "the datetime should be correctly displayed", }); // datepicker should not open on focus expect(".o_datetime_picker").toHaveCount(0); await click(".o_field_datetime input"); await animationFrame(); expect(".o_datetime_picker").toHaveCount(1); // select 22 April 2018 at 8:25 await zoomOut(); await zoomOut(); await click(getPickerCell("2018")); await animationFrame(); await click(getPickerCell("Apr")); await animationFrame(); await click(getPickerCell("22")); await animationFrame(); const [hourSelect, minuteSelect] = getTimePickers().at(0); await select("8", { target: hourSelect }); await animationFrame(); await select("25", { target: minuteSelect }); await animationFrame(); // Close the datepicker await click(".o_form_view_container"); await animationFrame(); expect(".o_datetime_picker").toHaveCount(0, { message: "datepicker should be closed" }); const newExpectedDateString = "04/22/2018 08:25:00"; expect(".o_field_datetime input").toHaveValue(newExpectedDateString, { message: "the selected date should be displayed in the input", }); // save await clickSave(); expect(".o_field_datetime input").toHaveValue(newExpectedDateString, { message: "the selected date should be displayed after saving", }); }); test("DatetimeField only triggers fieldChange when a day is picked and when an hour/minute is selected", async () => { mockTimeZone(+2); Partner._onChanges.datetime = () => {}; onRpc("onchange", () => expect.step("onchange")); await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ '
', }); await click(".o_field_datetime input"); await animationFrame(); expect(".o_datetime_picker").toHaveCount(1); // select 22 April 2018 at 8:25 await zoomOut(); await zoomOut(); await click(getPickerCell("2018")); await animationFrame(); await click(getPickerCell("Apr")); await animationFrame(); await click(getPickerCell("22")); await animationFrame(); expect.verifySteps([]); const [hourSelect, minuteSelect] = getTimePickers().at(0); await select("8", { target: hourSelect }); await animationFrame(); await select("25", { target: minuteSelect }); await animationFrame(); expect.verifySteps([]); // Close the datepicker await click(document.body); await animationFrame(); expect(".o_datetime_picker").toHaveCount(0); expect(".o_field_datetime input").toHaveValue("04/22/2018 08:25:00"); expect.verifySteps(["onchange"]); }); test("DatetimeField with datetime formatted without second", async () => { mockTimeZone(0); Partner._fields.datetime = fields.Datetime({ string: "A datetime", searchable: true, default: "2017-08-02 12:00:05", required: true, }); defineParams({ lang_parameters: { date_format: "%m/%d/%Y", time_format: "%H:%M", }, }); await mountView({ type: "form", resModel: "partner", arch: '
', }); const expectedDateString = "08/02/2017 12:00"; expect(".o_field_datetime input").toHaveValue(expectedDateString, { message: "the datetime should be correctly displayed", }); await click(".o_form_button_cancel"); expect(".modal").toHaveCount(0, { message: "there should not be a Warning dialog" }); }); test("DatetimeField in editable list view", async () => { mockTimeZone(+2); onRpc("has_group", () => true); await mountView({ type: "list", resModel: "partner", arch: /* xml */ ``, }); const expectedDateString = "02/08/2017 12:00:00"; // 10:00:00 without timezone expect("tr.o_data_row td:not(.o_list_record_selector):first").toHaveText(expectedDateString, { message: "the datetime should be correctly displayed", }); // switch to edit mode await click(".o_data_row .o_data_cell"); await animationFrame(); expect(".o_field_datetime input").toHaveCount(1, { message: "the view should have a date input for editable mode", }); expect(".o_field_datetime input").toBeFocused({ message: "date input should have the focus", }); expect(".o_field_datetime input").toHaveValue(expectedDateString, { message: "the date should be correct in edit mode", }); expect(".o_datetime_picker").toHaveCount(0); await click(".o_field_datetime input"); await animationFrame(); expect(".o_datetime_picker").toHaveCount(1); // select 22 April 2018 at 8:25 await zoomOut(); await zoomOut(); await click(getPickerCell("2018")); await animationFrame(); await click(getPickerCell("Apr")); await animationFrame(); await click(getPickerCell("22")); await animationFrame(); const [hourSelect, minuteSelect] = getTimePickers().at(0); await select("8", { target: hourSelect }); await animationFrame(); await select("25", { target: minuteSelect }); await animationFrame(); // Apply changes await click(getPickerApplyButton()); await animationFrame(); expect(".o_datetime_picker").toHaveCount(0, { message: "datepicker should be closed" }); const newExpectedDateString = "04/22/2018 08:25:00"; expect(".o_field_datetime input:first").toHaveValue(newExpectedDateString, { message: "the date should be correct in edit mode", }); // save await click(".o_list_button_save"); await animationFrame(); expect("tr.o_data_row td:not(.o_list_record_selector):first").toHaveText( newExpectedDateString, { message: "the selected datetime should be displayed after saving" } ); }); test.tags("desktop"); test("multi edition of DatetimeField in list view: edit date in input", async () => { onRpc("has_group", () => true); await mountView({ type: "list", resModel: "partner", arch: '', }); // select two records and edit them await click(".o_data_row:eq(0) .o_list_record_selector input"); await animationFrame(); await click(".o_data_row:eq(1) .o_list_record_selector input"); await animationFrame(); await click(".o_data_row:eq(0) .o_data_cell"); await animationFrame(); expect(".o_field_datetime input").toHaveCount(1); await click(".o_field_datetime input"); await edit("10/02/2019 09:00:00", { confirm: "Enter" }); await animationFrame(); expect(".modal").toHaveCount(1); await click(".modal .modal-footer .btn-primary"); await animationFrame(); expect(".o_data_row:first-child .o_data_cell:first").toHaveText("10/02/2019 09:00:00"); expect(".o_data_row:nth-child(2) .o_data_cell:first").toHaveText("10/02/2019 09:00:00"); }); test.tags("desktop"); test("multi edition of DatetimeField in list view: clear date in input", async () => { Partner._records[1].datetime = "2017-02-08 10:00:00"; onRpc("has_group", () => true); await mountView({ type: "list", resModel: "partner", arch: '', }); // select two records and edit them await click(".o_data_row:eq(0) .o_list_record_selector input"); await animationFrame(); await click(".o_data_row:eq(1) .o_list_record_selector input"); await animationFrame(); await click(".o_data_row:eq(0) .o_data_cell"); await animationFrame(); expect(".o_field_datetime input").toHaveCount(1); await click(".o_field_datetime input"); await animationFrame(); await edit("", { confirm: "Enter" }); await animationFrame(); expect(".modal").toHaveCount(1); await click(".modal .modal-footer .btn-primary"); await animationFrame(); expect(".o_data_row:first-child .o_data_cell:first").toHaveText(""); expect(".o_data_row:nth-child(2) .o_data_cell:first").toHaveText(""); }); test("DatetimeField remove value", async () => { expect.assertions(4); mockTimeZone(+2); onRpc("web_save", ({ args }) => { expect(args[1].datetime).toBe(false, { message: "the correct value should be saved" }); }); await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ '
', }); expect(".o_field_datetime input:first").toHaveValue("02/08/2017 12:00:00", { message: "the date should be correct in edit mode", }); await click(".o_field_datetime input"); await edit(""); await animationFrame(); await click(document.body); await animationFrame(); expect(".o_field_datetime input:first").toHaveValue("", { message: "should have an empty input", }); // save await clickSave(); expect(".o_field_datetime:first").toHaveText("", { message: "the selected date should be displayed after saving", }); }); test("DatetimeField with date/datetime widget (with day change) does not care about widget", async () => { mockTimeZone(-4); onRpc("has_group", () => true); Partner._records[0].p = [2]; Partner._records[1].datetime = "2017-02-08 02:00:00"; // UTC await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); const expectedDateString = "02/07/2017 22:00:00"; // local time zone expect(".o_field_widget[name='p'] .o_data_cell").toHaveText(expectedDateString, { message: "the datetime (datetime widget) should be correctly displayed in list view", }); // switch to form view await click(".o_field_widget[name='p'] .o_data_cell"); await animationFrame(); expect(".modal .o_field_date[name='datetime'] input").toHaveValue("02/07/2017 22:00:00", { message: "the datetime (date widget) should be correctly displayed in form view", }); }); test("DatetimeField with date/datetime widget (without day change) does not care about widget", async () => { mockTimeZone(-4); Partner._records[0].p = [2]; Partner._records[1].datetime = "2017-02-08 10:00:00"; // without timezone await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); const expectedDateString = "02/08/2017 06:00:00"; // with timezone expect(".o_field_widget[name='p'] .o_data_cell:first").toHaveText(expectedDateString, { message: "the datetime (datetime widget) should be correctly displayed in list view", }); // switch to form view await click(".o_field_widget[name='p'] .o_data_cell"); await animationFrame(); expect(".modal .o_field_date[name='datetime'] input:first").toHaveValue("02/08/2017 06:00:00", { message: "the datetime (date widget) should be correctly displayed in form view", }); }); test("datetime field: hit enter should update value", async () => { // This test verifies that the field datetime is correctly computed when: // - we press enter to validate our entry // - we click outside the field to validate our entry // - we save mockTimeZone(+2); await mountView({ type: "form", resModel: "partner", arch: '
', resId: 1, }); // Enter a beginning of date and press enter to validate await click(".o_field_datetime input"); await edit("01/08/22 14:30:40", { confirm: "Enter" }); const datetimeValue = `01/08/2022 14:30:40`; expect(".o_field_datetime input:first").toHaveValue(datetimeValue); // Click outside the field to check that the field is not changed await click(document.body); expect(".o_field_datetime input:first").toHaveValue(datetimeValue); // Save and check that it's still ok await clickSave(); expect(".o_field_datetime input:first").toHaveValue(datetimeValue); }); test("DateTimeField with label opens datepicker on click", async () => { await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `