vanilla 17.0

This commit is contained in:
Ernad Husremovic 2025-10-08 10:47:08 +02:00
parent d72e748793
commit a9bcec8e91
1986 changed files with 1613876 additions and 568976 deletions

View file

@ -34,10 +34,11 @@ QUnit.test("defaults", (assert) => {
filtersInfo: {},
formViewId: false,
hasEditDialog: false,
hasQuickCreate: true,
quickCreate: true,
quickCreateViewId: null,
isDateHidden: false,
isTimeHidden: false,
popoverFields: {},
popoverFieldNodes: {},
scale: "week",
scales: ["day", "week", "month", "year"],
showUnusualDays: false,
@ -87,15 +88,33 @@ QUnit.test("hasEditDialog", (assert) => {
check(assert, "event_open_popup", "0", "hasEditDialog", false);
});
QUnit.test("hasQuickCreate", (assert) => {
check(assert, "quick_add", "", "hasQuickCreate", true);
check(assert, "quick_add", "true", "hasQuickCreate", true);
check(assert, "quick_add", "True", "hasQuickCreate", true);
check(assert, "quick_add", "1", "hasQuickCreate", true);
check(assert, "quick_add", "false", "hasQuickCreate", false);
check(assert, "quick_add", "False", "hasQuickCreate", false);
check(assert, "quick_add", "0", "hasQuickCreate", false);
check(assert, "quick_add", "390", "hasQuickCreate", true);
QUnit.test("quickCreate", (assert) => {
check(assert, "quick_create", "", "quickCreate", true);
check(assert, "quick_create", "true", "quickCreate", true);
check(assert, "quick_create", "True", "quickCreate", true);
check(assert, "quick_create", "1", "quickCreate", true);
check(assert, "quick_create", "false", "quickCreate", false);
check(assert, "quick_create", "False", "quickCreate", false);
check(assert, "quick_create", "0", "quickCreate", false);
check(assert, "quick_create", "12", "quickCreate", true);
});
QUnit.test("quickCreateViewId", (assert) => {
let arch = parseArch(
`<calendar date_start="start_date" quick_create="0" quick_create_view_id="12" />`
);
assert.strictEqual(arch.quickCreate, false);
assert.strictEqual(arch.quickCreateViewId, null);
arch = parseArch(
`<calendar date_start="start_date" quick_create="1" quick_create_view_id="12" />`
);
assert.strictEqual(arch.quickCreate, true);
assert.strictEqual(arch.quickCreateViewId, 12);
arch = parseArch(`<calendar date_start="start_date" quick_create="1"/>`);
assert.strictEqual(arch.quickCreate, true);
assert.strictEqual(arch.quickCreateViewId, null);
});
QUnit.test("isDateHidden", (assert) => {

View file

@ -62,7 +62,7 @@ QUnit.module("CalendarView - CommonPopover", ({ beforeEach }) => {
});
const dateTimeGroup = target.querySelector(`.list-group`);
const dateTimeLabels = dateTimeGroup.textContent.replace(/\s+/g, " ").trim();
assert.strictEqual(dateTimeLabels, "July 16, 2021 (All day)");
assert.strictEqual(dateTimeLabels, "July 16, 2021");
});
QUnit.test("date duration: is all day and two days duration", async (assert) => {
@ -77,7 +77,7 @@ QUnit.module("CalendarView - CommonPopover", ({ beforeEach }) => {
});
const dateTimeGroup = target.querySelector(`.list-group`);
const dateTimeLabels = dateTimeGroup.textContent.replace(/\s+/g, " ").trim();
assert.strictEqual(dateTimeLabels, "July 16-17, 2021 (2 days)");
assert.strictEqual(dateTimeLabels, "July 16-17, 2021 2 days");
});
QUnit.test("time duration: 1 hour diff", async (assert) => {
@ -152,10 +152,7 @@ QUnit.module("CalendarView - CommonPopover", ({ beforeEach }) => {
});
const dateTimeGroup = target.querySelector(`.list-group`);
const dateTimeLabels = dateTimeGroup.textContent.replace(/\s+/g, " ").trim();
assert.strictEqual(
dateTimeLabels,
"July 16, 2021 08:00 - 11:15 (3 hours, 15 minutes)"
);
assert.strictEqual(dateTimeLabels, "July 16, 2021 08:00 - 11:15 (3 hours, 15 minutes)");
});
QUnit.test("isTimeHidden is true", async (assert) => {
@ -177,10 +174,7 @@ QUnit.module("CalendarView - CommonPopover", ({ beforeEach }) => {
});
const dateTimeGroup = target.querySelector(`.list-group`);
const dateTimeLabels = dateTimeGroup.textContent.replace(/\s+/g, " ").trim();
assert.strictEqual(
dateTimeLabels,
"July 16, 2021 08:00 - 11:15 (3 hours, 15 minutes)"
);
assert.strictEqual(dateTimeLabels, "July 16, 2021 08:00 - 11:15 (3 hours, 15 minutes)");
});
QUnit.test("canDelete is true", async (assert) => {

View file

@ -67,7 +67,9 @@ QUnit.module("CalendarView - CommonRenderer", ({ beforeEach }) => {
QUnit.test("Day: check date", async (assert) => {
await start({ model: { scale: "day" } });
assert.containsOnce(target, ".fc-day-header");
assert.strictEqual(target.querySelector(".fc-day-header").textContent, "July 16, 2021");
const dayHeader = target.querySelector(".fc-day-header");
assert.strictEqual(dayHeader.querySelector(".o_cw_day_name").textContent, "Friday");
assert.strictEqual(dayHeader.querySelector(".o_cw_day_number").textContent, "16");
});
QUnit.test("Day: click all day slot", async (assert) => {
@ -152,11 +154,35 @@ QUnit.module("CalendarView - CommonRenderer", ({ beforeEach }) => {
await start({ model: { scale: "week" } });
assert.containsN(target, ".fc-day-header", 7);
const dates = ["Sun 11", "Mon 12", "Tue 13", "Wed 14", "Thu 15", "Fri 16", "Sat 17"];
const dateNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const dates = ["11", "12", "13", "14", "15", "16", "17"];
const els = target.querySelectorAll(".fc-day-header");
for (let i = 0; i < els.length; i++) {
assert.strictEqual(els[i].textContent, dates[i]);
assert.strictEqual(els[i].querySelector(".o_cw_day_name").textContent, dateNames[i]);
assert.strictEqual(els[i].querySelector(".o_cw_day_number").textContent, dates[i]);
}
});
QUnit.test("Day: automatically scroll to 6am", async (assert) => {
// Make calendar scrollable
target.style.height = "500px";
await start({ model: { scale: "day" } });
const containerDimensions = target.querySelector(".fc-scroller").getBoundingClientRect();
const dayStartDimensions = target
.querySelector('tr[data-time="06:00:00"')
.getBoundingClientRect();
assert.ok(Math.abs(dayStartDimensions.y - containerDimensions.y) <= 2);
});
QUnit.test("Week: automatically scroll to 6am", async (assert) => {
// Make calendar scrollable
target.style.height = "500px";
await start({ model: { scale: "week" } });
const containerDimensions = target.querySelector(".fc-scroller").getBoundingClientRect();
const dayStartDimensions = target
.querySelector('tr[data-time="06:00:00"')
.getBoundingClientRect();
assert.ok(Math.abs(dayStartDimensions.y - containerDimensions.y) <= 2);
});
});

View file

@ -1,124 +0,0 @@
/** @odoo-module **/
import { CalendarDatePicker } from "@web/views/calendar/date_picker/calendar_date_picker";
import { click, getFixture, patchDate } from "../../helpers/utils";
import { makeEnv, makeFakeModel, mountComponent } from "./helpers";
let target;
async function start(params = {}) {
const { services, props, model: modelParams } = params;
const env = await makeEnv(services);
const model = makeFakeModel(modelParams);
return await mountComponent(CalendarDatePicker, env, {
model,
...props,
});
}
QUnit.module("CalendarView - DatePicker", ({ beforeEach }) => {
beforeEach(() => {
target = getFixture();
patchDate(2021, 7, 14, 8, 0, 0);
});
QUnit.test("Mount a CalendarDatePicker", async (assert) => {
await start({ model: { scale: "day" } });
assert.containsOnce(target, ".o_calendar_mini.hasDatepicker");
assert.strictEqual(target.querySelector(".o_selected_range").textContent, "16");
assert.containsOnce(target, `[data-month="6"][data-year="2021"] .o_selected_range`);
assert.strictEqual(target.querySelector(".ui-datepicker-month").textContent, "Jul");
assert.strictEqual(target.querySelector(".ui-datepicker-year").textContent, "2021");
assert.strictEqual(target.querySelector("thead").textContent, "SMTWTFS");
});
QUnit.test("Scale: init with day", async (assert) => {
await start({ model: { scale: "day" } });
assert.containsOnce(target, ".o_selected_range");
assert.containsOnce(target, "a.o_selected_range");
assert.strictEqual(target.querySelector(".o_selected_range").textContent, "16");
});
QUnit.test("Scale: init with week", async (assert) => {
await start({ model: { scale: "week" } });
assert.containsOnce(target, ".o_selected_range");
assert.containsOnce(target, "tr.o_selected_range");
assert.hasClass(target.querySelector("tr.o_selected_range"), "o_color");
assert.strictEqual(target.querySelector(".o_selected_range").textContent, "11121314151617");
});
QUnit.test("Scale: init with month", async (assert) => {
await start({ model: { scale: "month" } });
assert.containsN(target, "td.o_selected_range", 35);
});
QUnit.test("Scale: init with year", async (assert) => {
await start({ model: { scale: "year" } });
assert.containsN(target, "td.o_selected_range", 35);
});
QUnit.test("First day: 0 = Sunday", async (assert) => {
await start({ model: { scale: "day", firstDayOfWeek: 0 } });
assert.strictEqual(target.querySelector("thead").textContent, "SMTWTFS");
});
QUnit.test("First day: 1 = Monday", async (assert) => {
await start({ model: { scale: "day", firstDayOfWeek: 1 } });
assert.strictEqual(target.querySelector("thead").textContent, "MTWTFSS");
});
QUnit.test("Click on active day should change scale : day -> month", async (assert) => {
assert.expect(2);
await start({
model: {
scale: "day",
load(params) {
assert.strictEqual(params.scale, "month");
assert.ok(params.date.equals(luxon.DateTime.local(2021, 7, 16)));
},
},
});
await click(target, ".ui-state-active");
});
QUnit.test("Click on active day should change scale : month -> week", async (assert) => {
assert.expect(2);
await start({
model: {
scale: "month",
load(params) {
assert.strictEqual(params.scale, "week");
assert.ok(params.date.equals(luxon.DateTime.local(2021, 7, 16)));
},
},
});
await click(target, ".ui-state-active");
});
QUnit.test("Click on active day should change scale : week -> day", async (assert) => {
assert.expect(2);
await start({
model: {
scale: "week",
load(params) {
assert.strictEqual(params.scale, "day");
assert.ok(params.date.equals(luxon.DateTime.local(2021, 7, 16)));
},
},
});
await click(target, ".ui-state-active");
});
QUnit.test("Scale: today is correctly highlighted", async (assert) => {
patchDate(2021, 6, 4, 8, 0, 0);
await start({ model: { scale: "month" } });
assert.containsOnce(target, ".ui-datepicker-today");
assert.strictEqual(target.querySelector(".ui-datepicker-today").textContent, "4");
});
});

View file

@ -1,7 +1,7 @@
/** @odoo-module **/
import { CalendarFilterPanel } from "@web/views/calendar/filter_panel/calendar_filter_panel";
import { click, getFixture, triggerEvent } from "../../helpers/utils";
import { click, getFixture } from "../../helpers/utils";
import { makeEnv, makeFakeModel, mountComponent } from "./helpers";
let target;
@ -47,13 +47,13 @@ QUnit.module("CalendarView - FilterPanel", ({ beforeEach }) => {
assert.containsN(sections[0], ".o_calendar_filter_item", 4);
assert.strictEqual(
sections[0].textContent.trim(),
"AttendeesMitchell AdminMarc DemoBrandon FreemanEverybody's calendar"
"AttendeesMitchell AdminBrandon FreemanMarc DemoEverybody's calendar"
);
header = sections[1].querySelector(".o_cw_filter_label");
assert.strictEqual(header.textContent, "Users");
assert.containsN(sections[1], ".o_calendar_filter_item", 2);
assert.strictEqual(sections[1].textContent.trim(), "UsersMarc DemoBrandon Freeman");
assert.strictEqual(sections[1].textContent.trim(), "UsersBrandon FreemanMarc Demo");
});
QUnit.test("section can collapse", async (assert) => {
@ -101,12 +101,12 @@ QUnit.module("CalendarView - FilterPanel", ({ beforeEach }) => {
assert.hasAttrValue(
filters[1].querySelector(".o_cw_filter_avatar"),
"data-src",
"/web/image/res.partner/6/avatar_128"
"/web/image/res.partner/4/avatar_128"
);
assert.hasAttrValue(
filters[2].querySelector(".o_cw_filter_avatar"),
"data-src",
"/web/image/res.partner/4/avatar_128"
"/web/image/res.partner/6/avatar_128"
);
});
@ -148,7 +148,7 @@ QUnit.module("CalendarView - FilterPanel", ({ beforeEach }) => {
await click(filters[1], ".o_calendar_filter_item .o_remove");
await click(filters[2], ".o_calendar_filter_item .o_remove");
assert.verifySteps(["partner_ids 2", "partner_ids 1"]);
assert.verifySteps(["partner_ids 1", "partner_ids 2"]);
});
QUnit.test("click on filter", async (assert) => {
@ -172,42 +172,10 @@ QUnit.module("CalendarView - FilterPanel", ({ beforeEach }) => {
await click(filters[3], "input");
assert.verifySteps([
"partner_ids 3 false",
"partner_ids 6 true",
"partner_ids 4 false",
"partner_ids 6 true",
"partner_ids all true",
"partner_ids all false",
]);
});
QUnit.test("hover filter opens tooltip", async (assert) => {
await start({
services: {
popover: {
start: () => ({
add: (target, _, props) => {
assert.step(props.filter.label);
assert.step("" + props.filter.hasAvatar);
assert.step("" + props.filter.value);
return () => {
assert.step("popOver Closed");
};
},
}),
},
},
});
const section = target.querySelectorAll(".o_calendar_filter")[0];
const filters = section.querySelectorAll(".o_calendar_filter_item");
await triggerEvent(filters[0], null, "mouseenter");
assert.verifySteps(["Mitchell Admin", "true", "3"]);
await triggerEvent(filters[0], null, "mouseleave");
assert.verifySteps(["popOver Closed"]);
await triggerEvent(filters[3], null, "mouseenter");
assert.verifySteps([]);
await triggerEvent(filters[3], null, "mouseleave");
assert.verifySteps([]);
});
});

View file

@ -90,18 +90,18 @@ QUnit.module("CalendarView - YearPopover", ({ beforeEach }) => {
QUnit.test("group records", async (assert) => {
await start({});
assert.containsN(target, ".o_cw_body > div", 5);
assert.containsN(target, ".o_cw_body > a", 5);
assert.containsN(target, ".o_cw_body > div", 4);
assert.containsN(target, ".o_cw_body > a", 1);
const sectionTitles = target.querySelectorAll(".o_cw_body > div");
assert.strictEqual(sectionTitles[0].textContent.trim(), "July 16, 2021");
assert.strictEqual(sectionTitles[1].textContent.trim(), "July 13-17, 2021");
assert.strictEqual(sectionTitles[2].textContent.trim(), "July 15-17, 2021");
assert.strictEqual(sectionTitles[3].textContent.trim(), "July 15-19, 2021");
assert.strictEqual(sectionTitles[0].textContent.trim(), "July 16, 2021R114:00R2");
assert.strictEqual(sectionTitles[1].textContent.trim(), "July 13-17, 2021R4");
assert.strictEqual(sectionTitles[2].textContent.trim(), "July 15-17, 2021R3");
assert.strictEqual(sectionTitles[3].textContent.trim(), "July 15-19, 2021R5");
assert.strictEqual(
target.querySelector(".o_cw_body").textContent.trim(),
"July 16, 2021R114:00 R2July 13-17, 2021R4July 15-17, 2021R3July 15-19, 2021R5 Create"
"July 16, 2021R114:00R2July 13-17, 2021R4July 15-17, 2021R3July 15-19, 2021R5 Create"
);
});
@ -113,8 +113,8 @@ QUnit.module("CalendarView - YearPopover", ({ beforeEach }) => {
editRecord: () => assert.step("edit"),
},
});
assert.containsOnce(target, ".o_cw_body > a");
await click(target, ".o_cw_body > a");
assert.containsOnce(target, ".o_cw_body a.o_cw_popover_link");
await click(target, ".o_cw_body a.o_cw_popover_link");
assert.verifySteps(["edit"]);
});
});

View file

@ -34,18 +34,18 @@ QUnit.module("CalendarView - YearRenderer", ({ beforeEach }) => {
// check "title format"
assert.strictEqual(monthHeaders.length, 12);
const monthTitles = [
"Jan 2021",
"Feb 2021",
"Mar 2021",
"Apr 2021",
"January 2021",
"February 2021",
"March 2021",
"April 2021",
"May 2021",
"Jun 2021",
"Jul 2021",
"Aug 2021",
"Sep 2021",
"Oct 2021",
"Nov 2021",
"Dec 2021",
"June 2021",
"July 2021",
"August 2021",
"September 2021",
"October 2021",
"November 2021",
"December 2021",
];
for (let i = 0; i < 12; i++) {
assert.strictEqual(monthHeaders[i].textContent, monthTitles[i]);
@ -137,18 +137,24 @@ QUnit.module("CalendarView - YearRenderer", ({ beforeEach }) => {
await selectDateRange(target, "2021-07-02", "2021-07-05");
});
QUnit.test("display correct column header for days, independent of the timezone", async (assert) => {
// Regression test: when the system tz is somewhere in a negative GMT (in our example Alaska)
// the day headers of a months were incorrectly set. (S S M T W T F) instead of (S M T W T F S)
// if the first day of the week is Sunday.
patchTimeZone(-540); // UTC-9 = Alaska
QUnit.test(
"display correct column header for days, independent of the timezone",
async (assert) => {
// Regression test: when the system tz is somewhere in a negative GMT (in our example Alaska)
// the day headers of a months were incorrectly set. (S S M T W T F) instead of (S M T W T F S)
// if the first day of the week is Sunday.
patchTimeZone(-540); // UTC-9 = Alaska
await start({});
await start({});
const dayHeaders = target
.querySelector(".fc-month-container")
.querySelectorAll(".fc-day-header");
const dayHeaders = target
.querySelector(".fc-month-container")
.querySelectorAll(".fc-day-header");
assert.deepEqual([...dayHeaders].map((el) => el.textContent), ["S", "M", "T", "W", "T", "F", "S"]);
});
assert.deepEqual(
[...dayHeaders].map((el) => el.textContent),
["S", "M", "T", "W", "T", "F", "S"]
);
}
);
});

View file

@ -1,7 +1,9 @@
/** @odoo-module **/
import { uiService } from "@web/core/ui/ui_service";
import { createElement } from "@web/core/utils/xml";
import { registry } from "@web/core/registry";
import { Field } from "@web/views/fields/field";
import { clearRegistryWithCleanup, makeTestEnv } from "../../helpers/mock_env";
import { click, getFixture, mount, nextTick, triggerEvent } from "../../helpers/utils";
import { setupViewRegistries } from "@web/../tests/views/helpers";
@ -206,6 +208,8 @@ export const FAKE_FIELDS = {
};
function makeFakeModelState() {
const fakeFieldNode = createElement("field", { name: "name" });
const fakeModels = { event: FAKE_FIELDS };
return {
canCreate: true,
canDelete: true,
@ -226,9 +230,18 @@ function makeFakeModelState() {
isTimeHidden: false,
hasAllDaySlot: true,
hasEditDialog: false,
hasQuickCreate: false,
popoverFields: {
name: { rawAttrs: {}, options: {} },
quickCreate: false,
popoverFieldNodes: {
name: Field.parseFieldNode(fakeFieldNode, fakeModels, "event", "calendar"),
},
activeFields: {
name: {
context: "{}",
invisible: false,
readonly: false,
required: false,
onChange: false,
},
},
rangeEnd: makeFakeDate().endOf("month"),
rangeStart: makeFakeDate().startOf("month"),
@ -263,16 +276,15 @@ async function scrollTo(el, scrollParam) {
}
export function findPickedDate(target) {
return target.querySelector(".ui-datepicker-current-day");
return target.querySelector(".o_datetime_picker .o_selected");
}
export async function pickDate(target, date) {
const [year, month, day] = date.split("-");
const iMonth = parseInt(month, 10) - 1;
const day = date.split("-")[2];
const iDay = parseInt(day, 10) - 1;
const el = target.querySelectorAll(
`.ui-datepicker-calendar td[data-year="${year}"][data-month="${iMonth}"]`
)[iDay];
const el = target.querySelectorAll(`.o_datetime_picker .o_date_item_cell:not(.o_out_of_range)`)[
iDay
];
el.scrollIntoView();
await click(el);
}
@ -497,9 +509,40 @@ export async function resizeEventToTime(target, eventId, dateTime) {
await nextTick();
}
export async function resizeEventToDate(target, eventId, date) {
const event = findEvent(target, eventId);
const slot = findAllDaySlot(target, date);
await scrollTo(event);
await triggerEventForCalendar(event, "mouseenter");
// Find event resizer
const resizer = event.querySelector(".fc-end-resizer");
resizer.style.display = "block";
resizer.style.width = "100%";
resizer.style.height = "1em";
resizer.style.bottom = "0";
const resizerRect = resizer.getBoundingClientRect();
const resizerPos = {
x: resizerRect.x + resizerRect.width,
y: resizerRect.y + resizerRect.height / 2,
};
await triggerEventForCalendar(resizer, "mousedown", resizerPos);
// Find slot position
await scrollTo(slot, false);
const slotRect = slot.getBoundingClientRect();
const toPos = {
x: slotRect.x + slotRect.width / 2,
y: slotRect.y + slotRect.height / 2,
};
await triggerEventForCalendar(slot, "mousemove", toPos);
await triggerEventForCalendar(slot, "mouseup", toPos);
await nextTick();
}
export async function changeScale(target, scale) {
await click(target, `.o_calendar_scale_buttons .scale_button_selection`);
await click(target, `.o_calendar_scale_buttons .o_calendar_button_${scale}`);
await click(target, `.o_view_scale_selector .scale_button_selection`);
await click(target, `.o_view_scale_selector .o_scale_button_${scale}`);
await nextTick();
}