Odoo18-Base/addons/web/static/tests/views/fields/datetime_field.test.js
2025-01-06 10:57:38 +07:00

624 lines
19 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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: '<form><field name="datetime"/></form>',
});
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 */ '<form><field name="datetime"/></form>',
});
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: '<form><field name="datetime"/></form>',
});
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 */ `<list editable="bottom"><field name="datetime"/></list>`,
});
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: '<list multi_edit="1"><field name="datetime"/></list>',
});
// 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: '<list multi_edit="1"><field name="datetime"/></list>',
});
// 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 */ '<form><field name="datetime"/></form>',
});
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 */ `
<form>
<field name="p">
<list><field name="datetime" /></list>
<form><field name="datetime" widget="date" /></form>
</field>
</form>`,
});
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 */ `
<form>
<field name="p">
<list><field name="datetime" /></list>
<form><field name="datetime" widget="date" /></form>
</field>
</form>`,
});
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: '<form><field name="datetime"/></form>',
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 */ `
<form>
<label for="datetime" string="When is it" />
<field name="datetime" />
</form>`,
});
await click("label.o_form_label");
await animationFrame();
expect(".o_datetime_picker").toHaveCount(1, { message: "datepicker should be opened" });
});
test("datetime field: use picker with arabic numbering system", async () => {
defineParams({ lang: "ar_001" }); // Select Arab language
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ `<form string="Partners"><field name="datetime" /></form>`,
});
expect("[name=datetime] input:first").toHaveValue("٠٢/٠٨/٢٠١٧ ١١:٠٠:٠٠");
await click("[name=datetime] input");
await animationFrame();
await select(45, { target: getTimePickers()[0][1] });
await animationFrame();
expect("[name=datetime] input:first").toHaveValue("٠٢/٠٨/٢٠١٧ ١١:٤٥:٠٠");
});
test("datetime field in list view with show_seconds option", async () => {
mockTimeZone(+2);
onRpc("has_group", () => true);
await mountView({
type: "list",
resModel: "partner",
arch: /* xml */ `
<list>
<field name="datetime" widget="datetime" options="{'show_seconds': false}" string="show_seconds as false"/>
<field name="datetime" widget="datetime" string="show_seconds as true"/>
</list>`,
});
expect(queryAllTexts(".o_data_row:first .o_field_datetime")).toEqual([
"02/08/2017 12:00",
"02/08/2017 12:00:00",
]);
});
test("edit a datetime field in form view with show_seconds option", async () => {
mockTimeZone(+2);
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
<form>
<field name="datetime" widget="datetime" options="{'show_seconds': false}" string="show_seconds as false"/>
<field name="datetime" widget="datetime" string="show_seconds as true"/>
</form>`,
});
const [dateField1, dateField2] = queryAll(".o_input.cursor-pointer");
await click(dateField1);
await animationFrame();
expect(".o_time_picker_select").toHaveCount(3); // 3rd 'o_time_picker_select' is for the seconds
await edit("02/08/2017 11:00:00", { confirm: "Enter" });
await animationFrame();
expect(dateField1).toHaveValue("02/08/2017 11:00", {
message: "seconds should be hidden for showSeconds false",
});
expect(dateField2).toHaveValue("02/08/2017 11:00:00", {
message: "seconds should be visible for showSeconds true",
});
});
test("datetime field (with widget) in kanban with show_time option", async () => {
mockTimeZone(+2);
await mountView({
type: "kanban",
resModel: "partner",
arch: `
<kanban>
<templates>
<t t-name="card">
<field name="datetime" widget="datetime" options="{'show_time': false}"/>
</t>
</templates>
</kanban>`,
resId: 1,
});
expect(".o_kanban_record:first").toHaveText("02/08/2017");
});
test("datetime field in list with show_time option", async () => {
mockTimeZone(+2);
onRpc("has_group", () => true);
await mountView({
type: "list",
resModel: "partner",
arch: `
<list editable="bottom">
<field name="datetime" options="{'show_time': false}"/>
<field name="datetime" />
</list>
`,
});
const dates = queryAll(".o_field_cell");
expect(dates[0]).toHaveText("02/08/2017", {
message: "for date field only date should be visible with date widget",
});
expect(dates[1]).toHaveText("02/08/2017 12:00:00", {
message: "for datetime field only date should be visible with date widget",
});
await click(dates[0]);
await animationFrame();
expect(".o_field_datetime input:first").toHaveValue("02/08/2017 12:00:00", {
message: "for datetime field both date and time should be visible with datetime widget",
});
});
test("datetime field in form view with condensed option", async () => {
mockTimeZone(-2); // UTC-2
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: `
<form>
<field name="datetime" options="{'condensed': true}"/>
<field name="datetime" options="{'condensed': true}" readonly="1"/>
</form>`,
});
const expectedDateString = "2/8/2017 8:00:00"; // 10:00:00 without timezone
expect(".o_field_datetime input").toHaveValue(expectedDateString);
expect(".o_field_datetime.o_readonly_modifier").toHaveText(expectedDateString);
});
test("datetime field in kanban view with condensed option", async () => {
mockTimeZone(-2); // UTC-2
await mountView({
type: "kanban",
resModel: "partner",
arch: `
<kanban>
<templates>
<t t-name="card">
<field name="datetime" options="{'condensed': true}"/>
</t>
</templates>
</kanban>`,
});
const expectedDateString = "2/8/2017 8:00:00"; // 10:00:00 without timezone
expect(".o_kanban_record:first").toHaveText(expectedDateString);
});