/** @odoo-module **/
import { registerCleanup } from "../../helpers/cleanup";
import { defaultLocalization } from "../../helpers/mock_services";
import {
click,
editInput,
getFixture,
makeDeferred,
nextTick,
patchDate,
patchTimeZone,
patchWithCleanup,
triggerEvent,
} from "../../helpers/utils";
import {
changeScale,
clickDate,
clickEvent,
findEvent,
findFilterPanelFilter,
findFilterPanelSection,
findPickedDate,
findTimeRow,
moveEventToAllDaySlot,
moveEventToDate,
moveEventToTime,
navigate,
pickDate,
resizeEventToTime,
selectAllDayRange,
selectDateRange,
selectTimeRange,
toggleFilter,
toggleSectionFilter,
} from "../../views/calendar/helpers";
import { makeView, setupViewRegistries } from "../../views/helpers";
import { createWebClient, doAction } from "../../webclient/helpers";
import { browser } from "@web/core/browser/browser";
import { dialogService } from "@web/core/dialog/dialog_service";
import { localization } from "@web/core/l10n/localization";
import { registry } from "@web/core/registry";
import { userService } from "@web/core/user_service";
import { CalendarYearRenderer } from "@web/views/calendar/calendar_year/calendar_year_renderer";
import { CharField } from "@web/views/fields/char/char_field";
import { actionService } from "@web/webclient/actions/action_service";
const fieldRegistry = registry.category("fields");
const preloadedDataRegistry = registry.category("preloadedData");
const serviceRegistry = registry.category("services");
let target;
let serverData;
const uid = -1;
QUnit.module("Views", ({ beforeEach }) => {
beforeEach(() => {
// 2016-12-12 08:00:00
patchDate(2016, 11, 12, 8, 0, 0);
patchWithCleanup(browser, {
setTimeout: (fn) => fn(),
clearTimeout: () => {},
});
target = getFixture();
setupViewRegistries();
serviceRegistry.add(
"user",
{
...userService,
start() {
const fakeUserService = userService.start(...arguments);
Object.defineProperty(fakeUserService, "userId", {
get: () => uid,
});
return fakeUserService;
},
},
{ force: true }
);
serverData = {
models: {
event: {
fields: {
id: { string: "ID", type: "integer" },
user_id: {
string: "user",
type: "many2one",
relation: "user",
default: uid,
},
partner_id: {
string: "user",
type: "many2one",
relation: "partner",
related: "user_id.partner_id",
default: 1,
},
name: { string: "name", type: "char" },
start_date: { string: "start date", type: "date" },
stop_date: { string: "stop date", type: "date" },
start: { string: "start datetime", type: "datetime" },
stop: { string: "stop datetime", type: "datetime" },
delay: { string: "delay", type: "float" },
allday: { string: "allday", type: "boolean" },
partner_ids: {
string: "attendees",
type: "one2many",
relation: "partner",
default: [[6, 0, [1]]],
},
type: { string: "type", type: "integer" },
event_type_id: {
string: "Event Type",
type: "many2one",
relation: "event_type",
},
color_event: {
string: "Color",
type: "integer",
related: "event_type_id.color_event_type",
},
is_hatched: { string: "Hatched", type: "boolean" },
is_striked: { string: "Striked", type: "boolean" },
},
records: [
{
id: 1,
user_id: uid,
partner_id: 1,
name: "event 1",
start: "2016-12-11 00:00:00",
stop: "2016-12-11 00:00:00",
allday: false,
partner_ids: [1, 2, 3],
type: 1,
is_hatched: false,
},
{
id: 2,
user_id: uid,
partner_id: 1,
name: "event 2",
start: "2016-12-12 10:55:05",
stop: "2016-12-12 14:55:05",
allday: false,
partner_ids: [1, 2],
type: 3,
is_hatched: false,
},
{
id: 3,
user_id: 4,
partner_id: 4,
name: "event 3",
start: "2016-12-12 15:55:05",
stop: "2016-12-12 16:55:05",
allday: false,
partner_ids: [1],
type: 2,
is_hatched: true,
},
{
id: 4,
user_id: uid,
partner_id: 1,
name: "event 4",
start: "2016-12-14 15:55:05",
stop: "2016-12-14 18:55:05",
allday: true,
partner_ids: [1],
type: 2,
is_hatched: false,
is_striked: true,
},
{
id: 5,
user_id: 4,
partner_id: 4,
name: "event 5",
start: "2016-12-13 15:55:05",
stop: "2016-12-20 18:55:05",
allday: false,
partner_ids: [2, 3],
type: 2,
is_hatched: true,
},
{
id: 6,
user_id: uid,
partner_id: 1,
name: "event 6",
start: "2016-12-18 08:00:00",
stop: "2016-12-18 09:00:00",
allday: false,
partner_ids: [3],
type: 3,
is_hatched: true,
},
{
id: 7,
user_id: uid,
partner_id: 1,
name: "event 7",
start: "2016-11-14 08:00:00",
stop: "2016-11-16 17:00:00",
allday: false,
partner_ids: [2],
type: 1,
is_hatched: false,
},
],
methods: {
check_access_rights() {
return Promise.resolve(true);
},
},
},
user: {
fields: {
id: { string: "ID", type: "integer" },
display_name: { string: "Displayed name", type: "char" },
partner_id: {
string: "partner",
type: "many2one",
relation: "partner",
},
image: { string: "image", type: "integer" },
},
records: [
{ id: uid, display_name: "user 1", partner_id: 1 },
{ id: 4, display_name: "user 4", partner_id: 4 },
],
},
partner: {
fields: {
id: { string: "ID", type: "integer" },
display_name: { string: "Displayed name", type: "char" },
image: { string: "image", type: "integer" },
},
records: [
{ id: 1, display_name: "partner 1", image: "AAA" },
{ id: 2, display_name: "partner 2", image: "BBB" },
{ id: 3, display_name: "partner 3", image: "CCC" },
{ id: 4, display_name: "partner 4", image: "DDD" },
],
},
event_type: {
fields: {
id: { string: "ID", type: "integer" },
display_name: { string: "Displayed name", type: "char" },
color_event_type: { string: "Color", type: "integer" },
},
records: [
{ id: 1, display_name: "Event Type 1", color_event_type: 1 },
{ id: 2, display_name: "Event Type 2", color_event_type: 2 },
{ id: 3, display_name: "Event Type 3 (color 4)", color_event_type: 4 },
],
},
filter_partner: {
fields: {
id: { string: "ID", type: "integer" },
user_id: { string: "user", type: "many2one", relation: "user" },
partner_id: {
string: "partner",
type: "many2one",
relation: "partner",
},
partner_checked: { string: "checked", type: "boolean" },
},
records: [
{ id: 1, user_id: uid, partner_id: 1, partner_checked: true },
{ id: 2, user_id: uid, partner_id: 2, partner_checked: true },
{ id: 3, user_id: 4, partner_id: 3, partner_checked: true },
],
},
},
views: {
"event,false,form": `
`,
"event,1,form": `
`,
},
};
});
QUnit.module("CalendarView");
QUnit.test(`simple calendar rendering`, async (assert) => {
assert.expect(24);
serverData.models.event.records.push({
id: 8,
user_id: uid,
partner_id: false,
name: "event 7",
start: "2016-12-18 09:00:00",
stop: "2016-12-18 10:00:00",
allday: false,
partner_ids: [2],
type: 1,
});
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsOnce(
target,
".o_calendar_renderer .fc-view-container",
"should instance of fullcalendar"
);
// test view scales
assert.containsNone(
target,
".fc-event",
"By default, only the events of the current user are displayed (0 in this case)"
);
// display all events
await click(target, ".o_calendar_filter_item[data-value='all'] input");
assert.containsN(
target,
".fc-event",
9,
"should display 9 events on the week (4 event + 5 days event)"
);
assert.containsN(
target,
".o_calendar_sidebar tr:has(.ui-state-active) td",
7,
"week scale should highlight 7 days in mini calendar"
);
await click(target, ".scale_button_selection");
await click(target, ".o_calendar_button_day"); // display only one day
assert.containsN(target, ".fc-event", 2, "should display 2 events on the day");
assert.containsOnce(
target,
".o_calendar_sidebar .o_selected_range",
"should highlight the target day in mini calendar"
);
await click(target, ".scale_button_selection");
await click(target, ".o_calendar_button_month"); // display all the month
// We display the events or partner 1 2 and 4. Partner 2 has nothing and Event 6 is for partner 6 (not displayed)
await click(target, ".o_calendar_filter_item[data-value='all'] input");
await click(
target.querySelector(".o_calendar_filter .o_calendar_filter_item[data-value='1'] input")
);
await click(target, ".o_calendar_filter_item[data-value='2'] input");
assert.containsN(
target,
".fc-event",
7,
"should display 7 events on the month (5 events + 2 week event - 1 'event 6' is filtered + 1 'Undefined event')"
);
assert.containsN(
target,
".o_calendar_sidebar td a",
31,
"month scale should highlight all days in mini calendar"
);
// test filters
assert.containsN(
target,
".o_calendar_sidebar .o_calendar_filter",
2,
"should display 2 filters"
);
const typeFilter = target.querySelectorAll(".o_calendar_filter")[1];
assert.ok(!!typeFilter, "should display 'user' filter");
assert.containsN(
typeFilter,
".o_calendar_filter_item",
3,
"should display 3 filter items for 'user'"
);
// filters which has no value should show with string "Undefined", should not have any user image and should show at the last
let lastFilter;
{
const filters = typeFilter.querySelectorAll(".o_calendar_filter_item");
lastFilter = filters[filters.length - 1];
}
assert.strictEqual(
lastFilter.hasAttribute("data-value"),
false,
"filters having false value should be displayed at last in filter items"
);
assert.strictEqual(
lastFilter.querySelector(".o_cw_filter_title").textContent,
"Undefined",
"filters having false value should display 'Undefined' string"
);
assert.containsNone(
lastFilter,
"label img",
"filters having false value should not have any user image"
);
const attendeesFilter = target.querySelectorAll(".o_calendar_filter")[0];
assert.ok(!!attendeesFilter, "should display 'attendees' filter");
assert.containsN(
attendeesFilter,
".o_calendar_filter_item",
3,
"should display 3 filter items for 'attendees' who use write_model (checkall + 2 saved + Everything)"
);
assert.containsOnce(
attendeesFilter,
".o-autocomplete",
"should display one2many search bar for 'attendees' filter"
);
assert.containsN(
target,
".fc-event",
7,
"should display 7 events ('event 5' counts for 2 because it spans two weeks and thus generate two fc-event elements)"
);
await click(target.querySelectorAll(".o_calendar_filter input[type=checkbox]")[1]); // click on partner 2
assert.containsN(target, ".fc-event", 4, "should now only display 4 event");
await click(target.querySelectorAll(".o_calendar_filter input[type=checkbox]")[2]);
assert.containsNone(target, ".fc-event", "should not display any event anymore");
// test search bar in filter
await click(target, ".o_calendar_sidebar input[type=text]");
let autoCompleteItems = document.body.querySelectorAll("ul.ui-autocomplete li");
assert.strictEqual(
autoCompleteItems.length,
2,
"should display 2 choices in one2many autocomplete"
);
await click(autoCompleteItems[0]);
assert.containsN(
attendeesFilter,
".o_calendar_filter_item",
4,
"should display 4 filter items for 'attendees'"
);
await click(target, ".o_calendar_sidebar input[type=text]");
autoCompleteItems = document.body.querySelectorAll("ul.ui-autocomplete li");
assert.strictEqual(
autoCompleteItems[0].textContent,
"partner 4",
"should display the last choice in one2many autocomplete"
);
await click(target.querySelectorAll(".o_calendar_filter_item .o_remove")[1]);
assert.containsN(
attendeesFilter,
".o_calendar_filter_item",
3,
"click on remove then should display 3 filter items for 'attendees'"
);
});
QUnit.test("filter panel autocomplete: updates when typing", async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
const section = findFilterPanelSection(target, "partner_ids");
assert.containsNone(section, ".o-autocomplete--dropdown-menu");
assert.containsNone(section, ".o-autocomplete--dropdown-item");
await click(section, ".o-autocomplete--input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsN(section, ".o-autocomplete--dropdown-item", 2);
assert.deepEqual(
[...section.querySelectorAll(".o-autocomplete--dropdown-item")].map((el) =>
el.textContent.trim()
),
["partner 3", "partner 4"]
);
const input = section.querySelector(".o-autocomplete--input");
input.value = "partner 3";
await triggerEvent(section, ".o-autocomplete--input", "input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsOnce(section, ".o-autocomplete--dropdown-item");
assert.strictEqual(
section.querySelector(".o-autocomplete--dropdown-item").textContent.trim(),
"partner 3"
);
input.value = "a string that would yield to no result as it is too very much convoluted";
await triggerEvent(section, ".o-autocomplete--input", "input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsOnce(section, ".o-autocomplete--dropdown-item");
assert.strictEqual(
section.querySelector(".o-autocomplete--dropdown-item").textContent.trim(),
"No records"
);
});
QUnit.test("add a filter with the search more dialog", async (assert) => {
serverData.views["partner,false,search"] = ``;
serverData.views["partner,false,list"] = `
`;
serverData.models.partner.records.push(
{ id: 5, display_name: "foo partner 5" },
{ id: 6, display_name: "foo partner 6" },
{ id: 7, display_name: "foo partner 7" },
{ id: 8, display_name: "foo partner 8" },
{ id: 9, display_name: "foo partner 9" },
{ id: 10, display_name: "foo partner 10" },
{ id: 11, display_name: "foo partner 11" },
{ id: 12, display_name: "foo partner 12" },
{ id: 13, display_name: "foo partner 13" }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route) {
if (route.endsWith("/has_group")) {
return true;
}
},
});
const section = findFilterPanelSection(target, "partner_ids");
assert.containsN(section, ".o_calendar_filter_item", 3);
assert.deepEqual(
[...section.querySelectorAll(".o_calendar_filter_item")].map((el) =>
el.textContent.trim()
),
["partner 2", "partner 1", "Everything"]
);
// Open the autocomplete dropdown
assert.containsNone(section, ".o-autocomplete--dropdown-menu");
assert.containsNone(section, ".o-autocomplete--dropdown-item");
await click(section, ".o-autocomplete--input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsN(section, ".o-autocomplete--dropdown-item", 9);
assert.deepEqual(
[...section.querySelectorAll(".o-autocomplete--dropdown-item")].map((el) =>
el.textContent.trim()
),
[
"partner 3",
"partner 4",
"foo partner 5",
"foo partner 6",
"foo partner 7",
"foo partner 8",
"foo partner 9",
"foo partner 10",
"Search More...",
]
);
// Change the search term
const input = section.querySelector(".o-autocomplete--input");
input.value = "foo";
await triggerEvent(section, ".o-autocomplete--input", "input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsN(section, ".o-autocomplete--dropdown-item", 9);
assert.deepEqual(
[...section.querySelectorAll(".o-autocomplete--dropdown-item")].map((el) =>
el.textContent.trim()
),
[
"foo partner 5",
"foo partner 6",
"foo partner 7",
"foo partner 8",
"foo partner 9",
"foo partner 10",
"foo partner 11",
"foo partner 12",
"Search More...",
]
);
// Open the search more dialog
assert.containsNone(target, ".modal");
await click(section, ".o-autocomplete--dropdown-item:last-child");
assert.containsOnce(target, ".modal");
assert.containsN(target, ".modal .o_data_row", 9);
assert.deepEqual(
[...target.querySelectorAll(".modal .o_data_row")].map((el) => el.textContent.trim()),
[
"foo partner 5",
"foo partner 6",
"foo partner 7",
"foo partner 8",
"foo partner 9",
"foo partner 10",
"foo partner 11",
"foo partner 12",
"foo partner 13",
]
);
assert.containsOnce(target, ".modal .o_searchview_facet");
assert.strictEqual(
target.querySelector(".modal .o_searchview_facet").textContent.trim(),
"Quick search: foo"
);
// Choose a record
await click(target, ".modal .o_data_row:first-child > td:first-child");
assert.containsNone(target, ".modal");
assert.containsN(section, ".o_calendar_filter_item", 4);
assert.deepEqual(
[...section.querySelectorAll(".o_calendar_filter_item")].map((el) =>
el.textContent.trim()
),
["partner 2", "partner 1", "foo partner 5", "Everything"]
);
assert.strictEqual(input.value, "");
// Open the autocomplete dropdown
assert.containsNone(section, ".o-autocomplete--dropdown-menu");
assert.containsNone(section, ".o-autocomplete--dropdown-item");
await click(section, ".o-autocomplete--input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsN(section, ".o-autocomplete--dropdown-item", 9);
assert.deepEqual(
[...section.querySelectorAll(".o-autocomplete--dropdown-item")].map((el) =>
el.textContent.trim()
),
[
"partner 3",
"partner 4",
"foo partner 6",
"foo partner 7",
"foo partner 8",
"foo partner 9",
"foo partner 10",
"foo partner 11",
"Search More...",
]
);
// Change the search term
input.value = "foo";
await triggerEvent(section, ".o-autocomplete--input", "input");
assert.containsOnce(section, ".o-autocomplete--dropdown-menu");
assert.containsN(section, ".o-autocomplete--dropdown-item", 9);
assert.deepEqual(
[...section.querySelectorAll(".o-autocomplete--dropdown-item")].map((el) =>
el.textContent.trim()
),
[
"foo partner 6",
"foo partner 7",
"foo partner 8",
"foo partner 9",
"foo partner 10",
"foo partner 11",
"foo partner 12",
"foo partner 13",
"Search More...",
]
);
// Open the search more dialog
assert.containsNone(target, ".modal");
await click(section, ".o-autocomplete--dropdown-item:last-child");
assert.containsOnce(target, ".modal");
assert.containsN(target, ".modal .o_data_row", 8);
assert.deepEqual(
[...target.querySelectorAll(".modal .o_data_row")].map((el) => el.textContent.trim()),
[
"foo partner 6",
"foo partner 7",
"foo partner 8",
"foo partner 9",
"foo partner 10",
"foo partner 11",
"foo partner 12",
"foo partner 13",
]
);
assert.containsOnce(target, ".modal .o_searchview_facet");
assert.strictEqual(
target.querySelector(".modal .o_searchview_facet").textContent.trim(),
"Quick search: foo"
);
// Close the search more dialog without choosing a record
await click(target, ".modal .o_form_button_cancel");
assert.containsNone(target, ".modal");
assert.containsN(section, ".o_calendar_filter_item", 4);
assert.deepEqual(
[...section.querySelectorAll(".o_calendar_filter_item")].map((el) =>
el.textContent.trim()
),
["partner 2", "partner 1", "foo partner 5", "Everything"]
);
assert.strictEqual(input.value, "");
});
QUnit.test(
`delete attribute on calendar doesn't show delete button in popover`,
async (assert) => {
assert.expect(2);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.containsNone(
target,
".o_cw_popover .o_cw_popover_delete",
"should not have the 'Delete' Button"
);
}
);
QUnit.test(`breadcrumbs are updated with the displayed period`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// displays week mode by default
assert.strictEqual(
target.querySelector(".o_control_panel .breadcrumb-item.active").textContent,
"undefined (Dec 11 – 17, 2016)",
"should display the current week"
);
// switch to day mode
await changeScale(target, "day");
assert.strictEqual(
target.querySelector(".o_control_panel .breadcrumb-item.active").textContent,
"undefined (December 12, 2016)",
"should display the current day"
);
// switch to month mode
await changeScale(target, "month");
assert.strictEqual(
target.querySelector(".o_control_panel .breadcrumb-item.active").textContent,
"undefined (December 2016)",
"should display the current month"
);
// switch to year mode
await changeScale(target, "year");
assert.strictEqual(
target.querySelector(".o_control_panel .breadcrumb-item.active").textContent,
"undefined (2016)",
"should display the current year"
);
});
QUnit.test(`create and change events`, async (assert) => {
assert.expect(28);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "write") {
assert.deepEqual(
args[1],
{ name: "event 4 modified" },
"should update the record"
);
}
},
});
assert.containsOnce(target, ".fc-dayGridMonth-view", "should display in month mode");
// click on an existing event to open the formViewDialog
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_edit",
"popover should have an edit button"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_delete",
"popover should have a delete button"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_close",
"popover should have a close button"
);
await click(target, ".o_cw_popover .o_cw_popover_edit");
assert.containsOnce(
target,
".modal-body",
"should open the form view in dialog when click on event"
);
await editInput(target.querySelector(".modal-body input"), null, "event 4 modified");
await click(target, ".modal-footer .o_form_button_save");
assert.containsNone(target, ".modal-body", "save button should close the modal");
// create a new event, quick create only
await clickDate(target, "2016-12-13");
assert.containsOnce(
target,
".o-calendar-quick-create",
"should open the quick create dialog"
);
await editInput(target, ".o-calendar-quick-create--input", "new event in quick create");
await click(target, ".o-calendar-quick-create--create-btn");
assert.strictEqual(
findEvent(target, 8).textContent,
"new event in quick create",
"should display the new record after quick create"
);
assert.containsN(
target,
"td.fc-event-container[colspan]",
2,
"should the new record have only one day"
);
// create a new event, quick create only (validated by pressing enter key)
await clickDate(target, "2016-12-13");
assert.containsOnce(
target,
".o-calendar-quick-create",
"should open the quick create dialog"
);
await editInput(
target,
".o-calendar-quick-create--input",
"new event in quick create validated by pressing enter key."
);
await triggerEvent(target, ".o-calendar-quick-create--input", "keyup", { key: "Enter" });
assert.strictEqual(
findEvent(target, 9).textContent,
"new event in quick create validated by pressing enter key.",
"should display the new record by pressing enter key"
);
// create a new event and edit it
await clickDate(target, "2016-12-27");
assert.containsOnce(
target,
".o-calendar-quick-create",
"should open the quick create dialog"
);
await editInput(target, ".o-calendar-quick-create--input", "coucou");
await click(target, ".o-calendar-quick-create--edit-btn");
assert.containsOnce(target, ".modal", "should open the slow create dialog");
assert.strictEqual(
target.querySelector(".modal .modal-title").textContent,
"New Event",
"should use the string attribute as modal title"
);
assert.strictEqual(
target.querySelector(`.modal [name="name"] input`).value,
"coucou",
"should have set the name from the quick create dialog"
);
await click(target, ".modal-footer .o_form_button_save");
assert.strictEqual(
findEvent(target, 10).textContent,
"coucou",
"should display the new record with string attribute"
);
// create a new event with 2 days
await selectDateRange(target, "2016-12-20", "2016-12-21");
await editInput(target, ".o-calendar-quick-create--input", "new event in quick create 2");
await click(target, ".o-calendar-quick-create--edit-btn");
assert.strictEqual(
target.querySelector(`.modal .o_form_view [name="name"] input`).value,
"new event in quick create 2",
"should open the formViewDialog with default values"
);
await click(target, ".modal-footer .o_form_button_save");
assert.containsNone(target, ".modal", "should close dialogs");
const newEvent = findEvent(target, 11);
assert.strictEqual(
newEvent.textContent,
"new event in quick create 2",
"should display the 2 days new record"
);
assert.hasAttrValue(
newEvent.closest(".fc-event-container"),
"colspan",
"2",
"the new record should have 2 days"
);
await clickEvent(target, 11);
const popoverDescription = target.querySelector(".o_cw_popover .list-group-item");
assert.strictEqual(
popoverDescription.children[1].textContent,
"December 20-21, 2016",
"The popover description should indicate the correct range"
);
assert.strictEqual(
popoverDescription.children[2].textContent,
"(2 days)",
"The popover description should indicate 2 days"
);
await click(target, ".o_cw_popover_close");
// delete the a record
await clickEvent(target, 4);
await click(target, ".o_cw_popover_delete");
assert.strictEqual(
target.querySelector(".modal-title").textContent,
"Confirmation",
"should display the confirm message"
);
await click(target, ".modal-footer button.btn-primary");
assert.notOk(findEvent(target, 4), "the record should be deleted");
assert.containsN(target, ".fc-event-container .fc-event", 10, "should display 10 events");
// move to next month
await navigate(target, "next");
assert.containsNone(target, ".fc-event-container .fc-event", "should display 0 events");
await navigate(target, "prev");
await selectDateRange(target, "2016-12-20", "2016-12-21");
await editInput(target, ".o-calendar-quick-create--input", "test");
await click(target, ".o-calendar-quick-create--create-btn");
});
QUnit.test(`quickcreate with custom create_name_field`, async (assert) => {
assert.expect(4);
serverData.models.custom_event = {
fields: {
id: { string: "ID", type: "integer" },
x_name: { string: "name", type: "char" },
x_start_date: { string: "start date", type: "date" },
},
records: [{ id: 1, x_name: "some event", x_start_date: "2016-12-06" }],
methods: {
async check_access_rights() {
return true;
},
},
};
await makeView({
type: "calendar",
resModel: "custom_event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "create") {
assert.deepEqual(
args[0],
{
x_name: "custom event in quick create",
x_start_date: "2016-12-13",
},
"the custom create_name_field should be used instead of `name`"
);
}
},
});
// create a new event
await clickDate(target, "2016-12-13");
assert.containsOnce(
target,
".o-calendar-quick-create",
"should open the quick create dialog"
);
await editInput(target, ".o-calendar-quick-create--input", "custom event in quick create");
await click(target, ".o-calendar-quick-create--create-btn");
assert.containsOnce(
target,
`.fc-event[data-event-id="2"]`,
"should display the new custom event record"
);
assert.strictEqual(findEvent(target, 2).textContent, "custom event in quick create");
});
QUnit.test(`quickcreate switching to actual create for required fields`, async (assert) => {
assert.expect(4);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method }) {
if (method === "create") {
return Promise.reject({
message: {
code: 200,
data: {},
message: "Odoo server error",
},
event: new Event("server_error"),
});
}
},
});
// create a new event
await clickDate(target, "2016-12-13");
assert.strictEqual(
target.querySelector(".modal-title").textContent,
"New Event",
"should open the quick create dialog"
);
await editInput(target, ".o-calendar-quick-create--input", "custom event in quick create");
await click(target, ".o-calendar-quick-create--create-btn");
assert.containsNone(target, ".o-calendar-quick-create");
assert.strictEqual(
target.querySelector(".modal-title").textContent,
"New Event",
"should have switched to a bigger modal for an actual create rather than quickcreate"
);
assert.containsOnce(
target,
".modal .o_form_view .o_form_editable",
"should open the full event form view in a dialog"
);
});
QUnit.test(`open multiple event form at the same time`, async (assert) => {
let counter = 0;
patchWithCleanup(dialogService, {
start() {
const result = this._super(...arguments);
return {
...result,
add() {
counter++;
return result.add(...arguments);
},
};
},
});
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
for (let i = 0; i < 5; i++) {
await clickDate(target, "2016-12-13");
}
await nextTick();
assert.strictEqual(counter, 5, "there should had been 5 attemps to open a modal");
assert.containsOnce(target, ".modal", "there should be only one open modal");
});
QUnit.test(`create event with timezone in week mode European locale`, async (assert) => {
assert.expect(4);
serverData.models.event.records = [];
patchTimeZone(120);
patchWithCleanup(localization, defaultLocalization);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, args }) {
if (method === "create") {
assert.deepEqual(
args,
[
{
allday: false,
name: "new event",
start: "2016-12-13 06:00:00",
stop: "2016-12-13 08:00:00",
},
],
"should create this event"
);
}
},
});
await selectTimeRange(target, "2016-12-13 08:00:00", "2016-12-13 10:00:00");
assert.strictEqual(
target.querySelector(".fc-content .fc-time").textContent,
"8:00 - 10:00",
"should display the time in the calendar sticker"
);
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--create-btn");
assert.strictEqual(
target.querySelector(".fc-event .o_event_title").textContent,
"new event",
"should display the new event with title"
);
// delete record
await clickEvent(target, 1);
await click(target, ".o_cw_popover .o_cw_popover_delete");
await click(target, ".modal button.btn-primary");
assert.containsNone(target, ".fc-content", "should delete the record");
});
QUnit.test(`default week start (US)`, async (assert) => {
// if not given any option, default week start is on Sunday
assert.expect(3);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2016-12-17 22:59:59"],
["stop", ">=", "2016-12-10 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const dayHeaders = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
dayHeaders[0].textContent,
"Sun 11",
"The first day of the week should be Sunday"
);
assert.strictEqual(
dayHeaders[dayHeaders.length - 1].textContent,
"Sat 17",
"The last day of the week should be Saturday"
);
});
QUnit.test(`European week start`, async (assert) => {
assert.expect(3);
// the week start depends on the locale
patchWithCleanup(localization, { weekStart: 1 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
// called twice (once for records and once for filters)
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2016-12-18 22:59:59"],
["stop", ">=", "2016-12-11 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const dayHeaders = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
dayHeaders[0].textContent,
"Mon 12",
"The first day of the week should be Monday"
);
assert.strictEqual(
dayHeaders[dayHeaders.length - 1].textContent,
"Sun 18",
"The last day of the week should be Sunday"
);
});
QUnit.test(`week numbering`, async (assert) => {
// The week is now calculated by FullCalendar (ISO week). If it's start a sunday it
// returns the week of the monday.
patchWithCleanup(localization, { weekStart: 7 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.strictEqual(target.querySelector(".fc-week-number").textContent, "Week 50");
});
QUnit.test(`render popover`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.strictEqual(
target.querySelector(".o_cw_popover .popover-header").textContent,
"event 4",
"popover should have a title 'event 4'"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_edit",
"popover should have an edit button"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_delete",
"popover should have a delete button"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_close",
"popover should have a close button"
);
assert.strictEqual(
target.querySelector(".o_cw_popover .list-group-item b.text-capitalize").textContent,
"December 14, 2016",
"should display date 'December 14, 2016'"
);
assert.containsN(
target,
".o_cw_popover .o_cw_popover_fields_secondary .list-group-item",
2,
"popover should have a two fields"
);
const groups = target.querySelectorAll(
".o_cw_popover .o_cw_popover_fields_secondary .list-group-item"
);
assert.containsOnce(groups[0], ".o_field_char", "should apply char widget");
assert.strictEqual(
groups[0].querySelector("strong").textContent,
"Custom Name: ",
"label should be a 'Custom Name'"
);
assert.strictEqual(
groups[0].querySelector(".o_field_char").textContent,
"event 4",
"value should be a 'event 4'"
);
assert.containsOnce(groups[1], ".o_form_uri", "should apply m20 widget");
assert.strictEqual(
groups[1].querySelector("strong").textContent,
"user: ",
"label should be a 'user'"
);
assert.strictEqual(
groups[1].querySelector(".o_form_uri").textContent,
"partner 1",
"value should be a 'partner 1'"
);
await click(target, ".o_cw_popover .o_cw_popover_close");
assert.containsNone(target, ".o_cw_popover", "should close a popover");
});
QUnit.test(`render popover with modifiers`, async (assert) => {
serverData.models.event.fields.priority = {
string: "Priority",
type: "selection",
selection: [
["0", "Normal"],
["1", "Important"],
],
};
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.containsOnce(
target,
".o_cw_popover .o_priority span.o_priority_star",
"priority field should not be editable"
);
assert.containsNone(
target,
".o_cw_popover li.o_invisible_modifier",
"partner_id field should be invisible"
);
assert.containsOnce(
target,
".o_cw_popover .o_field_datetime",
"The start date and time should be visible"
);
await click(target, ".o_cw_popover .o_cw_popover_close");
assert.containsNone(target, ".o_cw_popover", "should close a popover");
});
QUnit.test(`render popover with widget which has specialData attribute`, async (assert) => {
assert.expect(3);
fieldRegistry.add("specialWidget", CharField);
preloadedDataRegistry.add("specialWidget", {
loadOnTypes: ["char"],
preload: () => {
assert.step("_fetchSpecialDataForMyWidget");
},
});
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.verifySteps(["_fetchSpecialDataForMyWidget"]);
});
QUnit.test("render popover: inside fullcalendar popover", async (assert) => {
assert.expect(13);
// add 10 records the same day
serverData.models.event.records = Array.from({ length: 10 }).map((_, i) => ({
id: i + 1,
name: `event ${i + 1}`,
start: "2016-12-14 10:00:00",
stop: "2016-12-14 15:00:00",
user_id: uid,
}));
let expectedRequest;
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.deepEqual(request, expectedRequest);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method }) {
if (method === "get_formview_id") {
return Promise.resolve(false);
}
},
});
const visibleEventsSelector = ":not(.fc-limited) > :not(.fc-limited) > .fc-event";
assert.containsN(target, visibleEventsSelector, 4);
assert.containsOnce(target, ".fc-more");
assert.strictEqual(target.querySelector(".fc-more").textContent, "+6 more");
assert.containsNone(target, ".fc-popover");
await click(target, ".fc-more");
assert.containsOnce(target, ".fc-popover");
assert.containsN(target, `.fc-popover ${visibleEventsSelector}`, 10);
assert.containsNone(target, ".o_cw_popover");
await click(target, ".fc-popover .fc-event:nth-child(1)");
assert.containsOnce(target, ".o_cw_popover");
await triggerEvent(target, ".o_cw_popover .o_cw_popover_edit", "mousedown");
assert.containsOnce(target, ".o_cw_popover");
assert.containsOnce(target, ".fc-popover");
expectedRequest = {
type: "ir.actions.act_window",
res_model: "event",
res_id: 1,
views: [[false, "form"]],
target: "current",
context: {},
};
await click(target, ".o_cw_popover .o_cw_popover_edit");
assert.containsNone(target, ".o_cw_popover");
assert.containsOnce(target, ".fc-popover");
});
QUnit.test(`attributes hide_date and hide_time`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
assert.containsNone(
target,
".o_cw_popover .list-group-item",
"popover should not contain date/time"
);
});
QUnit.test(
`create event with timezone in week mode with formViewDialog European locale`,
async (assert) => {
assert.expect(7);
patchWithCleanup(localization, defaultLocalization);
patchTimeZone(120);
serverData.models.event.records = [];
serverData.models.event.onchanges = {
allday(obj) {
if (obj.allday) {
obj.start_date = (obj.start && obj.start.split(" ")[0]) || obj.start_date;
obj.stop_date =
(obj.stop && obj.stop.split(" ")[0]) || obj.stop_date || obj.start_date;
} else {
obj.start = (obj.start_date && obj.start_date + " 00:00:00") || obj.start;
obj.stop =
(obj.stop_date && obj.stop_date + " 00:00:00") || obj.stop || obj.start;
}
},
};
let expectedEvent;
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, kwargs, method }) {
if (method === "create") {
assert.deepEqual(
kwargs.context,
{
default_name: "new event",
default_start: "2016-12-13 06:00:00",
default_stop: "2016-12-13 08:00:00",
default_allday: false,
lang: "en",
tz: "taht",
uid: 7,
},
"should send the context to create events"
);
} else if (method === "write") {
assert.deepEqual(args[1], expectedEvent, "should move the event");
}
},
});
await selectTimeRange(target, "2016-12-13 08:00:00", "2016-12-13 10:00:00");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--edit-btn");
let input = target.querySelector(".o_field_widget[name='start'] input");
assert.strictEqual(input.value, "12/13/2016 08:00:00", "should display the datetime");
// Set allday to true in formViewDialog
await click(target, ".modal .o_field_boolean[name='allday'] input");
input = target.querySelector(".o_field_widget[name='start_date'] input");
assert.strictEqual(input.value, "12/13/2016", "should display the date");
await click(target, ".modal .o_field_boolean[name='allday'] input");
input = target.querySelector(".o_field_widget[name='start'] input");
assert.strictEqual(
input.value,
"12/13/2016 02:00:00",
"should display the datetime from the date with the timezone"
);
// use datepicker to enter a date: 12/13/2016 08:00:00
await click(target, `.o_field_widget[name="start"] .o_datepicker .o_datepicker_input`);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]`
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .timepicker .timepicker-hour`
);
await click(
document.body.querySelectorAll(
`.bootstrap-datetimepicker-widget .timepicker-hours td.hour`
)[8]
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]`
);
// use datepicker to enter a date: 12/13/2016 10:00:00
await click(target, `.o_field_widget[name="stop"] .o_datepicker .o_datepicker_input`);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]`
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .timepicker .timepicker-hour`
);
await click(
document.body.querySelectorAll(
`.bootstrap-datetimepicker-widget .timepicker-hours td.hour`
)[10]
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]`
);
await click(target, ".modal-footer .o_form_button_save");
assert.strictEqual(
findEvent(target, 1).querySelector(".o_event_title").textContent,
"new event",
"should display the new event with title"
);
// Move this event to another day
expectedEvent = {
allday: false,
start: "2016-12-12 06:00:00",
stop: "2016-12-12 08:00:00",
};
await moveEventToTime(target, 1, "2016-12-12 08:00:00");
// Move to "All day"
expectedEvent = {
allday: true,
start: "2016-12-12",
stop: "2016-12-12",
};
await moveEventToAllDaySlot(target, 1, "2016-12-12");
}
);
QUnit.test(`create event with timezone in week mode American locale`, async (assert) => {
assert.expect(3);
patchWithCleanup(localization, defaultLocalization);
patchTimeZone(120);
serverData.models.event.records = [];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { kwargs, method }) {
if (method === "create") {
assert.deepEqual(
kwargs.context,
{
default_name: "new event",
default_start: "2016-12-13 04:00:00",
default_stop: "2016-12-13 06:00:00",
default_allday: false,
lang: "en",
tz: "taht",
uid: 7,
},
"should send the context to create events"
);
}
},
});
await selectTimeRange(target, "2016-12-13 06:00:00", "2016-12-13 08:00:00");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--create-btn");
assert.strictEqual(
findEvent(target, 1).querySelector(".o_event_title").textContent,
"new event",
"should display the new event with title"
);
// delete record
await clickEvent(target, 1);
await click(target, ".o_cw_popover .o_cw_popover_delete");
await click(target, ".modal button.btn-primary");
assert.containsNone(target, ".fc-content", "should delete the record");
});
QUnit.test(`fetch event when being in timezone`, async (assert) => {
assert.expect(3);
patchTimeZone(660);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { kwargs, method, model }) {
if (method === "search_read" && model === "event") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2016-12-17 12:59:59"],
["stop", ">=", "2016-12-10 13:00:00"],
],
"The domain should contain the right range"
);
}
},
});
const headers = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
headers[0].textContent,
"Sun 11",
"The calendar start date should be 2016-12-11"
);
assert.strictEqual(
headers[headers.length - 1].textContent,
"Sat 17",
"The calendar start date should be 2016-12-17"
);
});
QUnit.test(
`create event with timezone in week mode with formViewDialog American locale`,
async (assert) => {
assert.expect(7);
patchWithCleanup(localization, defaultLocalization);
patchTimeZone(120);
serverData.models.event.records = [];
serverData.models.event.onchanges = {
allday(obj) {
if (obj.allday) {
obj.start_date = (obj.start && obj.start.split(" ")[0]) || obj.start_date;
obj.stop_date =
(obj.stop && obj.stop.split(" ")[0]) || obj.stop_date || obj.start_date;
} else {
obj.start = (obj.start_date && obj.start_date + " 00:00:00") || obj.start;
obj.stop =
(obj.stop_date && obj.stop_date + " 00:00:00") || obj.stop || obj.start;
}
},
};
let expectedEvent = null;
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, kwargs, method }) {
if (method === "create") {
assert.deepEqual(
kwargs.context,
{
default_name: "new event",
default_start: "2016-12-13 06:00:00",
default_stop: "2016-12-13 08:00:00",
default_allday: false,
lang: "en",
tz: "taht",
uid: 7,
},
"should send the context to create events"
);
} else if (method === "write") {
assert.deepEqual(args[1], expectedEvent, "should move the event");
}
},
});
await selectTimeRange(target, "2016-12-13 08:00:00", "2016-12-13 10:00:00");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--edit-btn");
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start"] input`).value,
"12/13/2016 08:00:00",
"should display the datetime"
);
await click(target, `.modal .o_field_boolean[name="allday"] input`);
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start_date"] input`).value,
"12/13/2016",
"should display the date"
);
await click(target, `.modal .o_field_boolean[name="allday"] input`);
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start"] input`).value,
"12/13/2016 02:00:00",
"should display the datetime from the date with the timezone"
);
// use datepicker to enter a date: 12/13/2016 08:00:00
await click(target, `.o_field_widget[name="start"] .o_datepicker .o_datepicker_input`);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]`
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .timepicker .timepicker-hour`
);
await click(
document.body.querySelectorAll(
`.bootstrap-datetimepicker-widget .timepicker-hours td.hour`
)[8]
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]`
);
// use datepicker to enter a date: 12/13/2016 10:00:00
await click(target, `.o_field_widget[name="stop"] .o_datepicker .o_datepicker_input`);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]`
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .timepicker .timepicker-hour`
);
await click(
document.body.querySelectorAll(
`.bootstrap-datetimepicker-widget .timepicker-hours td.hour`
)[10]
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]`
);
await click(target, ".modal-footer button.btn-primary:not(.d-none)");
assert.strictEqual(
findEvent(target, 1).querySelector(".o_event_title").textContent,
"new event",
"should display the new event with title"
);
// Move this event to another day
expectedEvent = {
allday: false,
start: "2016-12-12 06:00:00",
stop: "2016-12-12 08:00:00",
};
await moveEventToTime(target, 1, "2016-12-12 08:00:00");
// Move to "All day"
expectedEvent = {
allday: true,
start: "2016-12-12",
stop: "2016-12-12",
};
await moveEventToAllDaySlot(target, 1, "2016-12-12");
}
);
QUnit.test(`check calendar week column timeformat`, async (assert) => {
patchWithCleanup(localization, { timeFormat: "hh:mm:ss" });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.strictEqual(
findTimeRow(target, "08:00:00").textContent,
"8am",
"calendar should show according to timeformat"
);
assert.strictEqual(
findTimeRow(target, "23:00:00").textContent,
"11pm",
"event time format should 12 hour"
);
});
QUnit.test(`create all day event in week mode`, async (assert) => {
assert.expect(3);
patchTimeZone(120);
serverData.models.event.records = [];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "create") {
assert.deepEqual(args[0], {
name: "new event",
start: "2016-12-14",
stop: "2016-12-15",
allday: true,
});
}
},
});
await selectAllDayRange(target, "2016-12-14", "2016-12-15");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--create-btn");
const event = findEvent(target, 1);
assert.strictEqual(
event.textContent.replace(/[\s\n\r]+/g, ""),
"newevent",
"should display the new event with time and title"
);
assert.hasAttrValue(event.parentElement, "colspan", "2", "should appear over two days.");
});
QUnit.test("create all day event in month mode: utc-11", async (assert) => {
assert.expect(3);
patchTimeZone(-660);
serverData.models.event.records = [];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "create") {
assert.deepEqual(args[0], {
name: "new event",
start: "2016-12-14",
stop: "2016-12-14",
allday: true,
});
}
},
});
await clickDate(target, "2016-12-14");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--create-btn");
const event = findEvent(target, 1);
assert.strictEqual(
event.textContent.replace(/[\s\n\r]+/g, ""),
"newevent",
"should display the new event with time and title"
);
const evBox = event.getBoundingClientRect();
const dateCell = target.querySelector(`[data-date="2016-12-14"]`);
const dtBox = dateCell.getBoundingClientRect();
assert.ok(
evBox.left >= dtBox.left &&
evBox.right <= dtBox.right &&
evBox.top >= dtBox.top &&
evBox.bottom <= dtBox.bottom,
"event should be inside the proper date cell"
);
});
QUnit.test("create all day event in year mode: utc-11", async (assert) => {
assert.expect(2);
patchTimeZone(-660);
serverData.models.event.records = [];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "create") {
assert.deepEqual(args[0], {
name: "new event",
start: "2016-12-14",
stop: "2016-12-14",
allday: true,
});
}
},
});
await clickDate(target, "2016-12-14");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--create-btn");
const event = findEvent(target, 1);
const evBox = event.getBoundingClientRect();
const dateCell = target.querySelector(`[data-date="2016-12-14"]`);
const dtBox = dateCell.getBoundingClientRect();
assert.ok(
evBox.left >= dtBox.left &&
evBox.right <= dtBox.right &&
evBox.top >= dtBox.top &&
evBox.bottom <= dtBox.bottom,
"event should be inside the proper date cell"
);
});
QUnit.test(`create event with default context (no quickCreate)`, async (assert) => {
assert.expect(3);
patchTimeZone(120);
serverData.models.event.records = [];
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.step("doAction");
assert.deepEqual(
request.context,
{
default_name: "New",
default_start: "2016-12-14",
default_stop: "2016-12-15",
default_allday: true,
lang: "en",
tz: "taht",
uid: 7,
},
"should send the correct data to create events"
);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
context: {
default_name: "New",
},
});
await selectAllDayRange(target, "2016-12-14", "2016-12-15");
assert.verifySteps(["doAction"]);
});
QUnit.test(`create event with default title in context (with quickCreate)`, async (assert) => {
assert.expect(1);
serverData.models.event.records = [];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
context: {
default_name: "Example Title",
},
});
await selectAllDayRange(target, "2016-12-14", "2016-12-15");
const input = target.querySelector(".o-calendar-quick-create--input");
assert.strictEqual(input.value, "Example Title");
});
QUnit.test(`create all day event in week mode (no quickCreate)`, async (assert) => {
assert.expect(1);
patchTimeZone(120);
serverData.models.event.records = [];
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.deepEqual(
request.context,
{
default_start: "2016-12-14",
default_stop: "2016-12-15",
default_allday: true,
lang: "en",
tz: "taht",
uid: 7,
},
"should send the correct data to create events"
);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await selectAllDayRange(target, "2016-12-14", "2016-12-15");
});
QUnit.test(`create event in month mode`, async (assert) => {
assert.expect(3);
patchTimeZone(120);
serverData.models.event.records = [];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "create") {
assert.deepEqual(
args[0],
{
name: "new event",
start: "2016-12-14 05:00:00",
stop: "2016-12-15 17:00:00",
},
"should send the correct data to create events"
);
}
},
});
await selectDateRange(target, "2016-12-14", "2016-12-15");
await editInput(target, ".o-calendar-quick-create--input", "new event");
await click(target, ".o-calendar-quick-create--create-btn");
const event = findEvent(target, 1);
assert.strictEqual(
event.textContent.replace(/[\s\n\r]+/g, ""),
"newevent",
"should display the new event with time and title"
);
assert.hasAttrValue(event.parentElement, "colspan", "2", "should appear over two days.");
});
QUnit.test(`use mini calendar`, async (assert) => {
patchTimeZone(120);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsOnce(target, ".fc-timeGridWeek-view", "should be in week mode");
assert.containsN(
target,
".fc-event",
9,
"should display 9 events on the week (4 event + 5 days event)"
);
await pickDate(target, "2016-12-19");
// Clicking on a day in another week should switch to the other week view
assert.containsOnce(target, ".fc-timeGridWeek-view", "should be in week mode");
assert.containsN(
target,
".fc-event",
4,
"should display 4 events on the week (1 event + 3 days event)"
);
// Clicking on a day in the same week should switch to that particular day view
await pickDate(target, "2016-12-18");
assert.containsOnce(target, ".fc-timeGridDay-view", "should be in day mode");
assert.containsN(target, ".fc-event", 2, "should display 2 events on the day");
// Clicking on the same day should toggle between day, month and week views
await pickDate(target, "2016-12-18");
assert.containsOnce(target, ".fc-dayGridMonth-view", "should be in month mode");
assert.containsN(
target,
".fc-event",
7,
"should display 7 events on the month (event 5 is on multiple weeks and generates to .fc-event)"
);
await pickDate(target, "2016-12-18");
assert.containsOnce(target, ".fc-timeGridWeek-view", "should be in week mode");
assert.containsN(
target,
".fc-event",
4,
"should display 4 events on the week (1 event + 3 days event)"
);
await pickDate(target, "2016-12-18");
assert.containsOnce(target, ".fc-timeGridDay-view", "should be in day mode");
assert.containsN(target, ".fc-event", 2, "should display 2 events on the day");
});
QUnit.test(`rendering, with many2many`, async (assert) => {
serverData.models.event.fields.partner_ids.type = "many2many";
serverData.models.event.records[0].partner_ids = [1, 2, 3, 4, 5];
serverData.models.partner.records.push({ id: 5, display_name: "partner 5", image: "EEE" });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsN(
target,
".o_calendar_filter_item .o_cw_filter_avatar",
3,
"should have 3 avatars in the side bar"
);
await toggleFilter(target, "partner_ids", "all");
// Event 1
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.containsOnce(target, ".o_cw_popover img", "should have 1 avatar");
// Event 2
await clickEvent(target, 1);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.containsN(target, ".o_cw_popover img", 5, "should have 5 avatar");
});
QUnit.test(`open form view`, async (assert) => {
assert.expect(2);
let expectedRequest;
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.deepEqual(request, expectedRequest);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method }) {
if (method === "get_formview_id") {
return Promise.resolve("A view");
}
},
});
// click on an existing event to open the form view
expectedRequest = {
type: "ir.actions.act_window",
res_id: 4,
res_model: "event",
views: [["A view", "form"]],
target: "current",
context: {},
};
await clickEvent(target, 4);
await click(target, ".o_cw_popover .o_cw_popover_edit");
// create a new event and edit it
await clickDate(target, "2016-12-27");
await editInput(target, ".o-calendar-quick-create--input", "coucou");
expectedRequest = {
type: "ir.actions.act_window",
res_model: "event",
views: [[false, "form"]],
target: "current",
context: {
default_name: "coucou",
default_start: "2016-12-27",
default_stop: "2016-12-27",
default_allday: true,
lang: "en",
tz: "taht",
uid: 7,
},
};
await click(target, ".o-calendar-quick-create--edit-btn");
});
QUnit.test(`create and edit event in month mode (all_day: false)`, async (assert) => {
assert.expect(1);
patchTimeZone(-240);
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.deepEqual(
request,
{
type: "ir.actions.act_window",
res_model: "event",
views: [[false, "form"]],
target: "current",
context: {
default_name: "coucou",
default_start: "2016-12-27 11:00:00", // 7:00 + 4h
default_stop: "2016-12-27 23:00:00", // 19:00 + 4h
default_allday: true,
lang: "en",
tz: "taht",
uid: 7,
},
},
"should open the form view with the context default values"
);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// create a new event and edit it
await clickDate(target, "2016-12-27");
await editInput(target, ".o-calendar-quick-create--input", "coucou");
await click(target, ".o-calendar-quick-create--edit-btn");
});
QUnit.test(`show start time of single day event for month mode`, async (assert) => {
patchTimeZone(-240);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.strictEqual(
findEvent(target, 2).querySelector(".fc-content .fc-time").textContent,
"06:55",
"should have a correct time 06:55 AM in month mode"
);
assert.containsNone(
findEvent(target, 4),
".fc-content .fc-time",
"should not display a time for all day event"
);
assert.containsNone(
findEvent(target, 5),
".fc-content .fc-time",
"should not display a time for multiple days event"
);
// switch to week mode
await changeScale(target, "week");
assert.containsOnce(
findEvent(target, 2),
".fc-content .fc-time",
"should show time in week mode"
);
// switch to day mode
await changeScale(target, "day");
assert.containsOnce(
findEvent(target, 2),
".fc-content .fc-time",
"should show time in day mode"
);
});
QUnit.test(`start time should not shown for date type field`, async (assert) => {
patchTimeZone(-240);
serverData.models.event.fields.start.type = "date";
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsNone(
findEvent(target, 2),
".fc-content .fc-time",
"should not show time for date type field"
);
});
QUnit.test(`start time should not shown in month mode if hide_time is true`, async (assert) => {
patchTimeZone(-240);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsNone(
findEvent(target, 2),
".fc-content .fc-time",
"should not show time for hide_time attribute"
);
});
QUnit.test(`readonly date_start field`, async (assert) => {
assert.expect(3);
serverData.models.event.fields.start.readonly = true;
let expectedRequest;
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.deepEqual(request, expectedRequest);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method }) {
if (method === "get_formview_id") {
return Promise.resolve(false);
}
},
});
assert.containsNone(target, ".fc-resizer", "should not have resize button");
expectedRequest = {
type: "ir.actions.act_window",
res_id: 4,
res_model: "event",
views: [[false, "form"]],
target: "current",
context: {},
};
await clickEvent(target, 4);
await click(target, ".o_cw_popover .o_cw_popover_edit");
// create a new event and edit it
await clickDate(target, "2016-12-27");
await editInput(target, ".o-calendar-quick-create--input", "coucou");
expectedRequest = {
type: "ir.actions.act_window",
res_model: "event",
views: [[false, "form"]],
target: "current",
context: {
default_name: "coucou",
default_start: "2016-12-27",
default_stop: "2016-12-27",
default_allday: true,
lang: "en",
tz: "taht",
uid: 7,
},
};
await click(target, ".o-calendar-quick-create--edit-btn");
});
QUnit.test(`check filters with filter_field specified`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsOnce(
findFilterPanelFilter(target, "partner_ids", 2),
"input:checked",
"checkbox should be checked"
);
await toggleFilter(target, "partner_ids", 2);
assert.containsNone(
findFilterPanelFilter(target, "partner_ids", 2),
"input:checked",
"checkbox should not be checked"
);
assert.strictEqual(
serverData.models.filter_partner.records.find((r) => r.id === 2).partner_checked,
false,
"the status of this filter should now be false"
);
await changeScale(target, "week"); // trick to reload the entire view
assert.containsNone(
findFilterPanelFilter(target, "partner_ids", 2),
"input:checked",
"checkbox should not be checked after the reload"
);
assert.strictEqual(
serverData.models.filter_partner.records.find((r) => r.id === 2).partner_checked,
false,
"the status of this filter should still be false after the reload"
);
});
QUnit.test('"all" filter', async (assert) => {
assert.expect(8);
let requestCount = 0;
const interval = [
["start", "<=", "2016-12-17 22:59:59"],
["stop", ">=", "2016-12-10 23:00:00"],
];
const expectedDomains = [
interval.concat([["partner_ids", "in", []]]),
interval.concat([["partner_ids", "in", [1]]]),
interval.concat([["partner_ids", "in", [1, 2]]]),
interval,
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { kwargs, method, model }) {
if (method === "search_read" && model === "event") {
assert.deepEqual(kwargs.domain, expectedDomains[requestCount]);
requestCount++;
}
},
});
// By default, no user is selected
assert.containsNone(target, ".fc-event", "should not display any event on the week");
await toggleFilter(target, "partner_ids", 1);
assert.containsN(target, ".fc-event", 4, "should display 4 events on the week");
await toggleFilter(target, "partner_ids", 2);
assert.containsN(target, ".fc-event", 9, "should display 9 events on the week");
// Click on the "all" filter to reload all events
await toggleFilter(target, "partner_ids", "all");
assert.containsN(target, ".fc-event", 9, "should display 9 events on the week");
});
QUnit.test("dynamic filters with selection fields", async (assert) => {
serverData.models.event.fields.selection = {
name: "selection",
string: "Ambiance",
type: "selection",
selection: [
["desert", "Desert"],
["forest", "Forest"],
],
};
serverData.models.event.records[0].selection = "forest";
serverData.models.event.records[1].selection = "desert";
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: /* xml */ `
`,
});
const section = findFilterPanelSection(target, "selection");
assert.deepEqual(section.querySelector(".o_cw_filter_label").textContent, "Ambiance");
assert.deepEqual(
[...section.querySelectorAll(".o_calendar_filter_item")].map((el) =>
el.textContent.trim()
),
["Forest", "Desert", "Undefined"]
);
});
QUnit.test("Colors: cycling through available colors", async (assert) => {
serverData.models.filter_partner.records = Array.from({ length: 56 }, (_, i) => ({
id: i + 1,
user_id: uid,
partner_id: i + 1,
partner_checked: true,
}));
serverData.models.partner.records = Array.from({ length: 56 }, (_, i) => ({
id: i + 1,
display_name: `partner ${i + 1}`,
}));
serverData.models.event.records = Array.from({ length: 56 }, (_, i) => ({
id: i + 1,
user_id: uid,
partner_id: i + 1,
name: `event ${i + 1}`,
start: `2016-12-12 0${i % 10}:00:00`,
stop: `2016-12-12 0${i % 10}:00:00`,
partner_ids: [i + 1],
}));
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsN(target, ".fc-event", 56);
assert.hasClass(findEvent(target, 1), "o_calendar_color_1");
assert.hasClass(findEvent(target, 55), "o_calendar_color_55");
assert.hasClass(findEvent(target, 56), "o_calendar_color_1");
const partnerSection = findFilterPanelSection(target, "partner_ids");
assert.containsOnce(partnerSection, ".o_calendar_filter_item[data-value='all']");
assert.containsN(partnerSection, ".o_calendar_filter_item:not([data-value='all'])", 56);
assert.hasClass(
partnerSection.querySelector(".o_calendar_filter_item[data-value='1']"),
"o_cw_filter_color_1"
);
assert.hasClass(
partnerSection.querySelector(".o_calendar_filter_item[data-value='55']"),
"o_cw_filter_color_55"
);
assert.hasClass(
partnerSection.querySelector(".o_calendar_filter_item[data-value='56']"),
"o_cw_filter_color_1"
);
});
QUnit.test("Colors: use available colors when attr is not number", async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
const colorClass = Array.from(findEvent(target, 1).classList).find((className) =>
className.startsWith("o_calendar_color_")
);
assert.notOk(isNaN(Number(colorClass.split("_").at(-1))));
await clickEvent(target, 1);
assert.hasClass(target.querySelector(".o_cw_popover"), colorClass);
});
QUnit.test(`Add filters and specific color`, async (assert) => {
serverData.models.event_type.records.push({
id: 4,
display_name: "Event Type no color",
color_event_type: 0,
});
serverData.models.event.records.push(
{
id: 8,
user_id: 4,
partner_id: 1,
name: "event 8",
start: "2016-12-11 09:00:00",
stop: "2016-12-11 10:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 3,
color_event: 4, // related is not managed by the mock server
},
{
id: 9,
user_id: 4,
partner_id: 1,
name: "event 9",
start: "2016-12-11 19:00:00",
stop: "2016-12-11 20:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 1,
color_event: 1, // related is not managed by the mock server
},
{
id: 10,
user_id: 4,
partner_id: 1,
name: "event 10",
start: "2016-12-11 12:00:00",
stop: "2016-12-11 13:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 4,
color_event: 0, // related is not managed by the mock server
}
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (route.startsWith("/web/static/lib/fullcalendar")) {
return;
}
if (kwargs.fields) {
assert.step(`${method} (${model}) [${(kwargs.fields || []).join(", ")}]`);
} else {
assert.step(`${method} (${model})`);
}
},
});
assert.verifySteps([
"get_views (event)",
"check_access_rights (event)",
"search_read (filter_partner) [partner_id]",
"search_read (event) [display_name, start, stop, allday, color_event, partner_ids, event_type_id]",
]);
// By default no filter is selected. We check before continuing.
await toggleFilter(target, "partner_ids", 1);
assert.verifySteps([
"search_read (filter_partner) [partner_id]",
"search_read (event) [display_name, start, stop, allday, color_event, partner_ids, event_type_id]",
]);
await toggleFilter(target, "partner_ids", 2);
assert.verifySteps([
"search_read (filter_partner) [partner_id]",
"search_read (event) [display_name, start, stop, allday, color_event, partner_ids, event_type_id]",
]);
assert.containsN(target, ".o_calendar_filter", 2, "should display 2 sections");
const typeSection = findFilterPanelSection(target, "event_type_id");
assert.strictEqual(
typeSection.querySelector(".o_cw_filter_label").textContent,
"Event Type",
"should display 'Event Type' filter"
);
assert.hasClass(findEvent(target, 8), "o_calendar_color_4");
assert.hasClass(findEvent(target, 9), "o_calendar_color_1");
assert.hasClass(findEvent(target, 10), "o_calendar_color_0");
assert.containsN(
typeSection,
".o_calendar_filter_item",
4,
"should display 4 filter items for 'Event Type'"
);
assert.containsOnce(
typeSection,
`.o_calendar_filter_item[data-value="3"].o_cw_filter_color_4`,
"Filter for event type 3 must have the color 4"
);
assert.containsOnce(
target,
`.fc-event[data-event-id="8"].o_calendar_color_4`,
"Event of event type 3 must have the color 4"
);
assert.containsOnce(
target,
`.fc-event[data-event-id="10"].o_calendar_color_0`,
"The first color is used when none is provided (default int field value being 0)"
);
});
QUnit.test(`Colors: dynamic filters without any color attr`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.hasClass(findEvent(target, 1), "o_calendar_color_0");
assert.hasClass(findEvent(target, 2), "o_calendar_color_0");
assert.hasClass(findEvent(target, 3), "o_calendar_color_0");
assert.hasClass(findEvent(target, 4), "o_calendar_color_0");
assert.hasClass(findEvent(target, 5), "o_calendar_color_0");
assert.containsOnce(target, ".o_calendar_filter[data-name=user_id]");
assert.containsNone(
findFilterPanelSection(target, "user_id"),
"[class*='o_cw_filter_color_']"
);
});
QUnit.test(`Colors: dynamic filters without color attr (related)`, async (assert) => {
serverData.models.event.records = [
{
id: 8,
user_id: 4,
partner_id: 1,
name: "event 8",
start: "2016-12-11 09:00:00",
stop: "2016-12-11 10:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 3,
color_event: 4, // related is not managed by the mock server
},
{
id: 9,
user_id: 4,
partner_id: 1,
name: "event 9",
start: "2016-12-11 19:00:00",
stop: "2016-12-11 20:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 1,
color_event: 1, // related is not managed by the mock server
},
{
id: 10,
user_id: 4,
partner_id: 1,
name: "event 10",
start: "2016-12-11 12:00:00",
stop: "2016-12-11 13:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 2,
color_event: 2, // related is not managed by the mock server
},
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model }) {
if (method === "search_read" && model === "event_type") {
throw new Error("should not fetch event_type filter colors");
}
},
});
await toggleSectionFilter(target, "partner_ids");
assert.hasClass(findEvent(target, 8), "o_calendar_color_4");
assert.hasClass(findEvent(target, 9), "o_calendar_color_1");
assert.hasClass(findEvent(target, 10), "o_calendar_color_2");
assert.containsNone(
findFilterPanelSection(target, "partner_ids"),
"[class*='o_cw_filter_color_']"
);
assert.containsN(
findFilterPanelSection(target, "event_type_id"),
"[class*='o_cw_filter_color_']",
3
);
});
QUnit.test(`Colors: dynamic filters without color attr (direct)`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model }) {
if (method === "search_read" && model === "event_type") {
throw new Error("should not fetch event_type filter colors");
}
},
});
assert.hasClass(findEvent(target, 1), "o_calendar_color_-1"); // uid = -1 ...
assert.hasClass(findEvent(target, 2), "o_calendar_color_-1"); // uid = -1 ...
assert.hasClass(findEvent(target, 3), "o_calendar_color_4");
assert.hasClass(findEvent(target, 4), "o_calendar_color_-1"); // uid = -1 ...
assert.hasClass(findEvent(target, 5), "o_calendar_color_4");
assert.containsNone(
findFilterPanelSection(target, "partner_id"),
"[class*='o_cw_filter_color_']"
);
assert.containsN(
findFilterPanelSection(target, "user_id"),
"[class*='o_cw_filter_color_']",
2
);
});
QUnit.test(`makeFilterUser: color for current user`, async (assert) => {
serverData.models["res.partner"] = {
fields: {
id: { string: "ID", type: "integer" },
display_name: { string: "Displayed name", type: "char" },
image: { string: "image", type: "integer" },
},
records: [
{ id: 1, display_name: "partner 1", image: "AAA" },
{ id: 2, display_name: "partner 2", image: "BBB" },
{ id: 3, display_name: "partner 3", image: "CCC" },
{ id: 4, display_name: "partner 4", image: "DDD" },
],
};
serverData.models.event.fields.partner_id.relation = "res.partner";
serverData.models.event.fields.partner_ids.relation = "res.partner";
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
const partnerSection = findFilterPanelSection(target, "partner_ids");
assert.containsN(partnerSection, "[class*='o_cw_filter_color_']", 3);
assert.strictEqual(
partnerSection.querySelector(".o_cw_filter_label").textContent,
"attendees"
);
assert.containsN(partnerSection, ".o_calendar_filter_item", 4);
assert.strictEqual(
partnerSection.querySelector(".o_calendar_filter_item[data-value='7']").textContent,
"Mitchell"
);
assert.containsOnce(
partnerSection,
`.o_calendar_filter_item[data-value="7"].o_cw_filter_color_7`
);
assert.containsOnce(
partnerSection,
`.o_calendar_filter_item[data-value="2"].o_cw_filter_color_2`
);
assert.containsOnce(
partnerSection,
`.o_calendar_filter_item[data-value="1"].o_cw_filter_color_1`
);
assert.strictEqual(
partnerSection.querySelector(".o_calendar_filter_item[data-value='all']").textContent,
"Everybody's calendars"
);
});
QUnit.test(`Colors: dynamic filters with same color as events`, async (assert) => {
serverData.models.event.records = [
{
id: 8,
user_id: 4,
partner_id: 1,
name: "event 8",
start: "2016-12-11 09:00:00",
stop: "2016-12-11 10:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 3,
color_event: 4, // related is not managed by the mock server
},
{
id: 9,
user_id: 4,
partner_id: 1,
name: "event 9",
start: "2016-12-11 19:00:00",
stop: "2016-12-11 20:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 1,
color_event: 1, // related is not managed by the mock server
},
{
id: 10,
user_id: 4,
partner_id: 1,
name: "event 10",
start: "2016-12-11 12:00:00",
stop: "2016-12-11 13:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 2,
color_event: 2, // related is not managed by the mock server
},
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model }) {
if (method === "search_read" && model === "event_type") {
throw new Error("should not fetch event_type filter colors");
}
},
});
await toggleSectionFilter(target, "partner_ids");
assert.hasClass(findEvent(target, 8), "o_calendar_color_4");
assert.hasClass(findEvent(target, 9), "o_calendar_color_1");
assert.hasClass(findEvent(target, 10), "o_calendar_color_2");
assert.containsN(
findFilterPanelSection(target, "event_type_id"),
"[class*='o_cw_filter_color_']",
3
);
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 1), "o_cw_filter_color_1");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 2), "o_cw_filter_color_2");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 3), "o_cw_filter_color_4");
});
QUnit.test(`Colors: dynamic filters with another color source`, async (assert) => {
serverData.models.event.records = [
{
id: 8,
user_id: 4,
partner_id: 3,
name: "event 8",
start: "2016-12-11 09:00:00",
stop: "2016-12-11 10:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 3,
color_event: 4, // related is not managed by the mock server
},
{
id: 9,
user_id: 4,
partner_id: 3,
name: "event 9",
start: "2016-12-11 19:00:00",
stop: "2016-12-11 20:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 1,
color_event: 1, // related is not managed by the mock server
},
{
id: 10,
user_id: 4,
partner_id: 3,
name: "event 10",
start: "2016-12-11 12:00:00",
stop: "2016-12-11 13:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 2,
color_event: 2, // related is not managed by the mock server
},
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model }) {
if (method === "search_read" && model === "event_type") {
assert.step("fetching event_type filter colors");
}
},
});
assert.verifySteps([]);
await toggleSectionFilter(target, "partner_ids");
assert.verifySteps(["fetching event_type filter colors"]);
assert.hasClass(findEvent(target, 8), "o_calendar_color_3");
assert.hasClass(findEvent(target, 9), "o_calendar_color_3");
assert.hasClass(findEvent(target, 10), "o_calendar_color_3");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 1), "o_cw_filter_color_1");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 2), "o_cw_filter_color_2");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 3), "o_cw_filter_color_4");
});
QUnit.test(`Colors: dynamic filters with no color source`, async (assert) => {
serverData.models.event.records = [
{
id: 8,
user_id: 4,
partner_id: 3,
name: "event 8",
start: "2016-12-11 09:00:00",
stop: "2016-12-11 10:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 3,
color_event: 4, // related is not managed by the mock server
},
{
id: 9,
user_id: 4,
partner_id: 3,
name: "event 9",
start: "2016-12-11 19:00:00",
stop: "2016-12-11 20:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 1,
color_event: 1, // related is not managed by the mock server
},
{
id: 10,
user_id: 4,
partner_id: 3,
name: "event 10",
start: "2016-12-11 12:00:00",
stop: "2016-12-11 13:00:00",
allday: false,
partner_ids: [1, 2, 3],
event_type_id: 2,
color_event: 2, // related is not managed by the mock server
},
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model }) {
if (method === "search_read" && model === "event_type") {
assert.step("fetching event_type filter colors");
}
},
});
assert.verifySteps([]);
await toggleSectionFilter(target, "partner_ids");
assert.verifySteps(["fetching event_type filter colors"]);
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 1), "o_cw_filter_color_1");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 2), "o_cw_filter_color_2");
assert.hasClass(findFilterPanelFilter(target, "event_type_id", 3), "o_cw_filter_color_4");
});
QUnit.test(`create event with filters`, async (assert) => {
serverData.models.event.fields.user_id.default = 5;
serverData.models.event.fields.partner_id.default = 3;
serverData.models.user.records.push({ id: 5, display_name: "user 5", partner_id: 3 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// By default only
await toggleFilter(target, "partner_ids", 1);
assert.containsN(target, ".o_calendar_filter_item", 5, "should display 5 filter items");
assert.containsN(target, ".fc-event", 4, "should display 4 events");
// quick create a record
await selectTimeRange(target, "2016-12-15 06:00:00", "2016-12-15 08:00:00");
await editInput(target, ".o-calendar-quick-create--input", "coucou");
await click(target, ".o-calendar-quick-create--create-btn");
assert.containsN(
target,
".o_calendar_filter_item",
6,
"should add the missing filter (active)"
);
assert.containsN(target, ".fc-event", 5, "should display the created item");
// change default value for quick create an hide record
serverData.models.event.fields.user_id.default = 4;
serverData.models.event.fields.partner_id.default = 4;
// Disable our filter to create a record without displaying it
await toggleFilter(target, "partner_id", 4);
// quick create and other record
await selectTimeRange(target, "2016-12-13 06:00:00", "2016-12-13 08:00:00");
await editInput(target, ".o-calendar-quick-create--input", "coucou 2");
await click(target, ".o-calendar-quick-create--create-btn");
assert.containsN(target, ".o_calendar_filter_item", 6, "should have the same filters");
assert.containsN(target, ".fc-event", 4, "should not display the created item");
await toggleFilter(target, "partner_id", 4);
await toggleFilter(target, "partner_ids", 2);
assert.containsN(target, ".fc-event", 11, "should display all records");
});
QUnit.test(`create event with filters (no quickCreate)`, async (assert) => {
serverData.views["event,false,form"] = `
`;
serverData.models.event.fields.user_id.default = 5;
serverData.models.event.fields.partner_id.default = 3;
serverData.models.user.records.push({ id: 5, display_name: "user 5", partner_id: 3 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// dislay all attendee calendars
await toggleSectionFilter(target, "partner_ids");
await toggleFilter(target, "partner_id", 4);
assert.containsN(target, ".o_calendar_filter_item", 5, "should display 5 filter items");
assert.containsN(target, ".fc-event", 3, "should display 3 events");
// quick create a record
await selectTimeRange(target, "2016-12-15 06:00:00", "2016-12-15 08:00:00");
await editInput(target, ".o-calendar-quick-create--input", "coucou");
await click(target, ".o-calendar-quick-create--edit-btn");
await click(target, ".modal-footer .o_form_button_save");
assert.containsN(
target,
".o_calendar_filter_item",
6,
"should add the missing filter (active)"
);
assert.containsN(target, ".fc-event", 4, "should display the created item");
});
QUnit.test(`Update event with filters`, async (assert) => {
const records = serverData.models.user.records;
records.push({ id: 5, display_name: "user 5", partner_id: 3 });
serverData.models.event.onchanges = {
user_id(obj) {
obj.partner_id = records.find((r) => r.id === obj.user_id).partner_id;
},
};
serverData.views["event,false,form"] = `
`;
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// select needed partner filters
await toggleFilter(target, "partner_ids", 1);
await toggleFilter(target, "partner_id", 4);
assert.containsN(target, ".o_calendar_filter_item", 5, "should display 5 filter items");
assert.containsN(target, ".fc-event", 3, "should display 3 events");
await clickEvent(target, 2);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
await click(target, ".o_cw_popover .o_cw_popover_edit");
assert.strictEqual(
target.querySelector(".modal .modal-title").textContent,
"Open: event 2",
"dialog should have a valid title"
);
await click(target, `.modal .o_field_widget[name="user_id"] input`);
await click(
document.body.querySelectorAll(".ui-autocomplete.dropdown-menu .ui-menu-item")[2]
);
await click(target, ".modal .o_form_button_save");
assert.containsN(
target,
".o_calendar_filter_item",
6,
"should add the missing filter (active)"
);
assert.containsN(target, ".fc-event", 3, "should display the updated item");
// test the behavior of the 'select all' input checkbox
assert.containsN(
target,
".o_calendar_filter_item input:checked",
3,
"should display 3 active checkbox"
);
assert.containsN(
target,
".o_calendar_filter_item input:not(:checked)",
3,
"should display 3 inactive checkbox"
);
// Click to select all users
await toggleSectionFilter(target, "partner_id");
// should contains 4 events
assert.containsN(target, ".fc-event", 4, "should display the updated events");
// Should have 4 checked boxes
assert.containsN(
target,
".o_calendar_filter_item input:checked",
4, // 3
"should display 4 active checkbox"
);
// unselect all user
await toggleSectionFilter(target, "partner_id");
assert.containsN(target, ".fc-event", 0, "should not display any event");
assert.containsN(
target,
".o_calendar_filter_item input:checked",
1,
"should display 1 active checkbox"
);
});
QUnit.test(`change pager with filters`, async (assert) => {
serverData.models.user.records.push({ id: 5, display_name: "user 5", partner_id: 3 });
serverData.models.event.records.push(
{
id: 8,
user_id: 5,
partner_id: 3,
name: "event 8",
start: "2016-12-06 04:00:00",
stop: "2016-12-06 08:00:00",
allday: false,
partner_ids: [1, 2, 3],
type: 1,
},
{
id: 9,
user_id: uid,
partner_id: 1,
name: "event 9",
start: "2016-12-07 04:00:00",
stop: "2016-12-07 08:00:00",
allday: false,
partner_ids: [1, 2, 3],
type: 1,
},
{
id: 10,
user_id: 4,
partner_id: 4,
name: "event 10",
start: "2016-12-08 04:00:00",
stop: "2016-12-08 08:00:00",
allday: false,
partner_ids: [1, 2, 3],
type: 1,
}
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// select filter for partner 1, 2 and 4
await toggleSectionFilter(target, "partner_ids");
await toggleFilter(target, "partner_id", 4);
await navigate(target, "prev");
assert.containsN(target, ".o_calendar_filter_item", 6, "should display 6 filter items");
assert.containsN(target, ".fc-event", 2, "should display 2 events");
const events = target.querySelectorAll(".fc-event .o_event_title");
assert.strictEqual(
Array.from(events)
.map((e) => e.textContent)
.join("")
.replace(/\s/g, ""),
"event8event9",
"should display 2 events"
);
});
QUnit.test(`ensure events are still shown if filters give an empty domain`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await toggleSectionFilter(target, "partner_ids");
assert.containsN(target, ".fc-event", 5, "should display 5 events");
await toggleFilter(target, "partner_ids", "all");
assert.containsN(target, ".fc-event", 5, "should display 5 events");
});
QUnit.test(`events starting at midnight`, async (assert) => {
patchWithCleanup(localization, defaultLocalization);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// Click on Tuesday 12am
await selectTimeRange(target, "2016-12-13 00:00:00", "2016-12-13 00:30:00");
assert.containsOnce(
target,
".o-calendar-quick-create",
"should open the quick create dialog"
);
// Creating the event
await editInput(
target.querySelector(".modal-body input"),
null,
"new event in quick create"
);
await click(target, ".o-calendar-quick-create--create-btn");
assert.strictEqual(
findEvent(target, 8).querySelector(".o_event_title").textContent,
"new event in quick create",
"should display the new record after quick create dialog"
);
});
QUnit.test(`set event as all day when field is date`, async (assert) => {
assert.expect(2);
patchTimeZone(-480); // UTC-8
serverData.models.event.records[0].start_date = "2016-12-14";
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await toggleFilter(target, "partner_ids", 1);
assert.containsOnce(
target,
".fc-day-grid .fc-event-container",
"should be one event in the all day row"
);
await clickEvent(target, 1);
assert.strictEqual(
target.querySelector(".o_cw_popover .list-group-item").textContent,
"December 14, 2016 (All day)"
);
});
QUnit.test(
`set event as all day when field is date (without all_day mapping)`,
async (assert) => {
assert.expect(1);
serverData.models.event.records[0].start_date = "2016-12-14";
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsOnce(
target,
".fc-day-grid .fc-event-container",
"should be one event in the all day row"
);
}
);
QUnit.test(`quickcreate avoid double event creation`, async (assert) => {
assert.expect(1);
let createCount = 0;
const def = makeDeferred();
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
async mockRPC(route, { method }, performRPC) {
if (method === "create") {
createCount++;
await def;
return performRPC(...arguments);
}
},
});
// create a new event
await clickDate(target, "2016-12-13");
await editInput(
target.querySelector(".modal-body input"),
null,
"new event in quick create"
);
// Simulate ENTER pressed on Create button (after a TAB)
await triggerEvent(target.querySelector(".modal-body input"), null, "keyup", {
key: "Enter",
});
await click(target, ".o-calendar-quick-create--create-btn");
def.resolve();
await nextTick();
assert.strictEqual(createCount, 1, "should create only one event");
});
QUnit.test(`calendar is configured to have no groupBy menu`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsNone(
target,
".o_control_panel .o_group_by_menu",
"the control panel has no groupBy menu"
);
});
QUnit.test(`timezone does not affect current day`, async (assert) => {
assert.expect(2);
patchTimeZone(2400); // 40 hours timezone
patchDate(2016, 11, 12, 8, 0, 0); // 2016-12-12 08:00:00
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.strictEqual(
findPickedDate(target).textContent,
"12",
"should highlight the target day"
);
await pickDate(target, "2016-12-11");
assert.strictEqual(
findPickedDate(target).textContent,
"11",
"should highlight the selected day"
);
});
QUnit.test(`timezone does not affect drag and drop`, async (assert) => {
assert.expect(10);
patchTimeZone(-2400); // UTC-40
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, args }) {
if (method === "write") {
assert.deepEqual(args[0], [6], "event 6 is moved");
assert.strictEqual(
args[1].start,
"2016-11-29 08:00:00",
"event moved to 27th nov 16h00 +40 hours timezone"
);
}
},
});
assert.strictEqual(findEvent(target, 1).textContent.replace(/\s/g, ""), "08:00event1");
await clickEvent(target, 1);
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start"]`).textContent,
"12/09/2016 08:00:00"
);
assert.strictEqual(findEvent(target, 6).textContent.replace(/\s/g, ""), "16:00event6");
await clickEvent(target, 6);
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start"]`).textContent,
"12/16/2016 16:00:00"
);
// Move event 6 as on first day of month view (27th november 2016)
await moveEventToDate(target, 6, "2016-11-27");
assert.strictEqual(findEvent(target, 6).textContent.replace(/\s/g, ""), "16:00event6");
await clickEvent(target, 6);
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start"]`).textContent,
"11/27/2016 16:00:00"
);
assert.strictEqual(findEvent(target, 1).textContent.replace(/\s/g, ""), "08:00event1");
await clickEvent(target, 1);
assert.strictEqual(
target.querySelector(`.o_field_widget[name="start"]`).textContent,
"12/09/2016 08:00:00"
);
});
QUnit.test(`timzeone does not affect calendar with date field`, async (assert) => {
assert.expect(11);
patchTimeZone(120);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, args }) {
if (method === "create") {
assert.strictEqual(args[0].start_date, "2016-12-20");
}
if (method === "write") {
assert.step(args[1].start_date);
}
},
});
// Create event (on 20 december)
await clickDate(target, "2016-12-20");
await editInput(target, ".o-calendar-quick-create--input", "An event");
await click(target, ".o-calendar-quick-create--create-btn");
await clickEvent(target, 8);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.strictEqual(
target.querySelector(
".o_cw_popover .o_cw_popover_fields_secondary .list-group-item .o_field_date"
).textContent,
"12/20/2016",
"should have correct start date"
);
// Move event to another day (on 27 november)
await moveEventToDate(target, 8, "2016-11-27");
assert.verifySteps(["2016-11-27"]);
await clickEvent(target, 8);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.strictEqual(
target.querySelector(
".o_cw_popover .o_cw_popover_fields_secondary .list-group-item .o_field_date"
).textContent,
"11/27/2016",
"should have correct start date"
);
// Move event to last day (on 7 january)
await moveEventToDate(target, 8, "2017-01-07");
assert.verifySteps(["2017-01-07"]);
await clickEvent(target, 8);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.strictEqual(
target.querySelector(
".o_cw_popover .o_cw_popover_fields_secondary .list-group-item .o_field_date"
).textContent,
"01/07/2017",
"should have correct start date"
);
});
QUnit.test(`drag and drop on month mode`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// Create event (on 20 december)
await clickDate(target, "2016-12-20");
await editInput(target, ".modal-body .o_field_widget[name=name] input", "An event");
await click(target, ".modal .o_form_button_save");
await moveEventToDate(target, 1, "2016-12-19", { disableDrop: true });
assert.hasClass(findEvent(target, 1), "dayGridMonth");
// Move event to another day (on 19 december)
await moveEventToDate(target, 8, "2016-12-19");
await clickEvent(target, 8);
const row = target.querySelectorAll(".o_cw_body .list-group-item")[1];
assert.strictEqual(
row.textContent.trim(),
"07:00 - 19:00 (12 hours)",
"start and end hours shouldn't have been changed"
);
});
QUnit.test(`drag and drop on month mode with all_day mapping`, async (assert) => {
// Same test as before but in calendarEventToRecord (calendar_model.js) there is
// different condition branching with all_day mapping or not
assert.expect(1);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// Create event (on 20 december)
await clickDate(target, "2016-12-20");
await editInput(target.querySelector(".modal-body input"), null, "An event");
await click(target.querySelector(`.o_field_widget[name="allday"] input`));
// use datepicker to enter a date: 12/20/2016 07:00:00
await click(target, `.o_field_widget[name="start"] .o_datepicker .o_datepicker_input`);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]`
);
await click(document.body, `.bootstrap-datetimepicker-widget .timepicker .timepicker-hour`);
await click(
document.body.querySelectorAll(
`.bootstrap-datetimepicker-widget .timepicker-hours td.hour`
)[7]
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]`
);
// use datepicker to enter a date: 12/20/2016 19:00:00
await click(target, `.o_field_widget[name="stop"] .o_datepicker .o_datepicker_input`);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]`
);
await click(document.body, `.bootstrap-datetimepicker-widget .timepicker .timepicker-hour`);
await click(
document.body.querySelectorAll(
`.bootstrap-datetimepicker-widget .timepicker-hours td.hour`
)[19]
);
await click(
document.body,
`.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]`
);
await click(target.querySelector(".modal .o_form_button_save"));
// Move event to another day (on 19 december)
await moveEventToDate(target, 8, "2016-12-19");
await clickEvent(target, 8);
const row = target.querySelectorAll(".o_cw_body .list-group-item")[1];
assert.strictEqual(
row.textContent.trim(),
"07:00 - 19:00 (12 hours)",
"start and end hours shouldn't have been changed"
);
});
QUnit.test(`drag and drop on month mode with date_start and date_delay`, async (assert) => {
assert.expect(1);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "write") {
// delay should not be written at drag and drop
assert.strictEqual(args[1].delay, undefined);
}
},
});
// Create event (on 20 december)
await clickDate(target, "2016-12-20");
await editInput(target.querySelector(".modal-body input"), null, "An event");
await click(target, ".o-calendar-quick-create--create-btn");
// Move event to another day (on 27 november)
await moveEventToDate(target, 8, "2016-11-27");
});
QUnit.test(`form_view_id attribute works (for creating events)`, async (assert) => {
assert.expect(1);
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.strictEqual(
request.views[0][0],
42,
"should do a do_action with view id 42"
);
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method }) {
if (method === "create") {
// we simulate here the case where a create call with just
// the field name fails. This is a normal flow, the server
// reject the create rpc (quick create), then the web client
// fall back to a form view. This happens typically when a
// model has required fields
return Promise.reject("None shall pass!");
}
},
});
await clickDate(target, "2016-12-13");
await editInput(target.querySelector(".modal-body input"), null, "It's just a fleshwound");
await click(target, ".o-calendar-quick-create--create-btn");
});
QUnit.test(`form_view_id attribute works with popup (for creating events)`, async (assert) => {
assert.expect(1);
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.strictEqual(request.views[0][0], 1, "should load view with id 1");
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickDate(target, "2016-12-13");
});
QUnit.test(`calendar fallback to form view id in action if necessary`, async (assert) => {
assert.expect(1);
serviceRegistry.add(
"action",
{
...actionService,
start() {
const result = actionService.start(...arguments);
const doAction = result.doAction;
result.doAction = (request) => {
assert.deepEqual(request, {
type: "ir.actions.act_window",
res_model: "event",
views: [[43, "form"]], // should use the view id from the config
target: "current",
context: {
lang: "en",
uid: 7,
tz: "taht",
default_name: "It's just a fleshwound",
default_start: "2016-12-13 06:00:00",
default_stop: "2016-12-13 18:00:00",
default_allday: true,
},
});
return doAction(request);
};
return result;
},
},
{ force: true }
);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: ``,
config: { views: [[43, "form"]] },
mockRPC(route, { method }) {
if (method === "create") {
// we simulate here the case where a create call with just
// the field name fails. This is a normal flow, the server
// reject the create rpc (quick create), then the web client
// fall back to a form view. This happens typically when a
// model has required fields
return Promise.reject("None shall pass!");
}
},
});
await clickDate(target, "2016-12-13");
await editInput(target.querySelector(".modal-body input"), null, "It's just a fleshwound");
await click(target, ".o-calendar-quick-create--create-btn");
});
QUnit.test(`fullcalendar initializes with right locale`, async (assert) => {
// The machine that runs this test must have this locale available
patchWithCleanup(luxon.Settings, { defaultLocale: "fr" });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: ``,
});
assert.deepEqual(
[...target.querySelectorAll(".fc-day-header")].map((el) => el.textContent),
["dim. 11", "lun. 12", "mar. 13", "mer. 14", "jeu. 15", "ven. 16", "sam. 17"]
);
});
QUnit.test(`initial_date given in the context`, async (assert) => {
assert.expect(1);
serverData.views = {
"event,1,calendar": ``,
"event,false,search": ``,
};
serverData.actions = {
1: {
id: 1,
name: "context initial date",
res_model: "event",
type: "ir.actions.act_window",
views: [[1, "calendar"]],
context: { initial_date: "2016-01-30 08:00:00" }, // 30th of january
},
};
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1);
await nextTick();
assert.strictEqual(
target.querySelector(".o_control_panel .breadcrumb-item").textContent,
"context initial date (January 30, 2016)",
"should display day passed in the context"
);
});
QUnit.test(`default week start (US) month mode`, async (assert) => {
// if not given any option, default week start is on Sunday
assert.expect(8);
// 2019-09-12 08:00:00
patchDate(2019, 8, 12, 8, 0, 0);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2019-10-12 22:59:59"],
["stop", ">=", "2019-08-31 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const dayHeaders = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
dayHeaders[0].textContent,
"Sunday",
"The first day of the week should be Sunday"
);
assert.strictEqual(
dayHeaders[dayHeaders.length - 1].textContent,
"Saturday",
"The last day of the week should be Saturday"
);
const dayTops = target.querySelectorAll(".fc-day-top");
assert.strictEqual(
dayTops[0].querySelector(".fc-week-number").textContent,
"36",
"The number of the week should be correct"
);
assert.strictEqual(dayTops[0].querySelector(".fc-day-number").textContent, "1");
assert.strictEqual(dayTops[0].dataset.date, "2019-09-01");
assert.strictEqual(
dayTops[dayTops.length - 1].querySelector(".fc-day-number").textContent,
"12"
);
assert.strictEqual(dayTops[dayTops.length - 1].dataset.date, "2019-10-12");
});
QUnit.test(`European week start month mode`, async (assert) => {
assert.expect(8);
patchDate(2019, 8, 15, 8, 0, 0); // 2019-09-15 08:00:00
// the week start depends on the locale
patchWithCleanup(localization, { weekStart: 1 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2019-10-06 22:59:59"],
["stop", ">=", "2019-08-25 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const dayHeaders = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
dayHeaders[0].textContent,
"Monday",
"The first day of the week should be Monday"
);
assert.strictEqual(
dayHeaders[dayHeaders.length - 1].textContent,
"Sunday",
"The last day of the week should be Sunday"
);
const dayTops = target.querySelectorAll(".fc-day-top");
assert.strictEqual(
dayTops[0].querySelector(".fc-week-number").textContent,
"35",
"The number of the week should be correct"
);
assert.strictEqual(dayTops[0].querySelector(".fc-day-number").textContent, "26");
assert.strictEqual(dayTops[0].dataset.date, "2019-08-26");
assert.strictEqual(
dayTops[dayTops.length - 1].querySelector(".fc-day-number").textContent,
"6"
);
assert.strictEqual(dayTops[dayTops.length - 1].dataset.date, "2019-10-06");
});
QUnit.test(`Monday week start week mode`, async (assert) => {
assert.expect(4);
patchDate(2019, 8, 15, 8, 0, 0); // 2019-09-15 08:00:00
// the week start depends on the locale
patchWithCleanup(localization, { weekStart: 1 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2019-09-15 22:59:59"],
["stop", ">=", "2019-09-08 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const dayHeaders = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
dayHeaders[0].textContent,
"Mon 9",
"The first day of the week should be Monday the 9th"
);
assert.strictEqual(
dayHeaders[dayHeaders.length - 1].textContent,
"Sun 15",
"The last day of the week should be Sunday the 15th"
);
assert.strictEqual(
target.querySelector(".fc-head .fc-week-number").textContent,
"Week 37",
"The number of the week should be correct"
);
});
QUnit.test(`Saturday week start week mode`, async (assert) => {
assert.expect(4);
patchDate(2019, 8, 12, 8, 0, 0); // 2019-09-12 08:00:00
// the week start depends on the locale
patchWithCleanup(localization, { weekStart: 6 });
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2019-09-13 22:59:59"],
["stop", ">=", "2019-09-06 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const dayHeaders = target.querySelectorAll(".fc-day-header");
assert.strictEqual(
dayHeaders[0].textContent,
"Sat 7",
"The first day of the week should be Saturday the 7th"
);
assert.strictEqual(
dayHeaders[dayHeaders.length - 1].textContent,
"Fri 13",
"The last day of the week should be Friday the 13th"
);
assert.strictEqual(
target.querySelector(".fc-head .fc-week-number").textContent,
"Week 36",
"The number of the week should be correct"
);
});
QUnit.test(`Monday week start year mode`, async (assert) => {
assert.expect(4);
patchDate(2019, 8, 15, 8, 0, 0); // 2019-09-15 08:00:00
// the week start depends on the locale
patchWithCleanup(localization, { weekStart: 1 });
patchWithCleanup(CalendarYearRenderer.prototype, {
get options() {
return { ...this._super(), weekNumbers: true };
},
});
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2019-12-31 22:59:59"],
["stop", ">=", "2018-12-31 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const weekRow = target.querySelector(".fc-day-top.fc-today").closest("tr");
const weekDays = weekRow.querySelectorAll(".fc-day-top");
assert.strictEqual(
weekDays[0].textContent,
"9",
"The first day of the week should be Monday the 9th"
);
assert.strictEqual(
weekDays[weekDays.length - 1].textContent,
"15",
"The last day of the week should be Sunday the 15th"
);
assert.strictEqual(
weekRow.querySelector(".fc-week-number").textContent,
"37",
"The number of the week should be correct"
);
});
QUnit.test(`Sunday week start year mode`, async (assert) => {
assert.expect(4);
patchDate(2019, 8, 15, 8, 0, 0); // 2019-09-15 08:00:00
// the week start depends on the locale
// the localization presents a python-like 1 to 7 weekStart value
patchWithCleanup(localization, { weekStart: 7 });
patchWithCleanup(CalendarYearRenderer.prototype, {
get options() {
return { ...this._super(), weekNumbers: true };
},
});
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, model, kwargs }) {
if (model === "event" && method === "search_read") {
assert.deepEqual(
kwargs.domain,
[
["start", "<=", "2019-12-31 22:59:59"],
["stop", ">=", "2018-12-31 23:00:00"],
],
"The domain to search events in should be correct"
);
}
},
});
const weekRow = target.querySelector(".fc-day-top.fc-today").closest("tr");
const weekDays = weekRow.querySelectorAll(".fc-day-top");
assert.strictEqual(
weekDays[0].textContent,
"15",
"The first day of the week should be Sunday the 15th"
);
assert.strictEqual(
weekDays[weekDays.length - 1].textContent,
"21",
"The last day of the week should be Saturday the 21st"
);
assert.strictEqual(
weekRow.querySelector(".fc-week-number").textContent,
"38",
"The number of the week should be correct"
);
});
QUnit.test(
`edit record and attempt to create a record with "create" attribute set to false`,
async (assert) => {
assert.expect(8);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "write") {
assert.deepEqual(
args[1],
{ name: "event 4 modified" },
"should update the record"
);
}
},
});
// editing existing events should still be possible
// click on an existing event to open the formViewDialog
await clickEvent(target, 4);
assert.containsOnce(target, ".o_cw_popover", "should open a popover clicking on event");
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_edit",
"popover should have an edit button"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_delete",
"popover should have a delete button"
);
assert.containsOnce(
target,
".o_cw_popover .o_cw_popover_close",
"popover should have a close button"
);
await click(target, ".o_cw_popover .o_cw_popover_edit");
assert.containsOnce(
target,
".modal-body",
"should open the form view in dialog when click on edit"
);
await editInput(target.querySelector(".modal-body input"), null, "event 4 modified");
await click(target.querySelector(".modal-footer .o_form_button_save"));
assert.containsNone(target, ".modal", "save button should close the modal");
// creating an event should not be possible
// attempt to create a new event with create set to false
await clickDate(target, "2016-12-13");
assert.containsNone(
target,
".modal",
"shouldn't open a quick create dialog for creating a new event with create attribute set to false"
);
}
);
QUnit.test(
`attempt to create record with "create" and "quick_add" attributes set to false`,
async (assert) => {
assert.expect(1);
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
// attempt to create a new event with create set to false
await clickDate(target, "2016-12-13");
assert.containsNone(
target,
".modal",
"shouldn't open a form view for creating a new event with create attribute set to false"
);
}
);
QUnit.test(
`attempt to create multiples events and the same day and check the ordering on month view`,
async (assert) => {
// This test aims to verify that the order of the event in month view is coherent with their start date.
patchDate(2020, 2, 12, 8, 0, 0); // 2020-03-12 08:00:00
serverData.models.event.records = [
{
id: 1,
name: "Second event",
start: "2020-03-12 05:00:00",
stop: "2020-03-12 07:00:00",
allday: false,
},
{
id: 2,
name: "First event",
start: "2020-03-12 02:00:00",
stop: "2020-03-12 03:00:00",
allday: false,
},
{
id: 3,
name: "Third event",
start: "2020-03-12 08:00:00",
stop: "2020-03-12 09:00:00",
allday: false,
},
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsOnce(
target,
".o_calendar_renderer .fc-view-container",
"should display in the calendar"
);
// Testing the order of the events: by start date
assert.containsN(target, ".o_event_title", 3, "3 events should be available");
assert.strictEqual(
target.querySelector(".o_event_title").textContent,
"First event",
"First event should be on top"
);
}
);
QUnit.test(`create event and resize to next day (24h) on week mode`, async (assert) => {
// WOWL FYI Legacy test name: "drag and drop 24h event on week mode"
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { args, method }) {
if (method === "create") {
assert.deepEqual(args[0], {
allday: false,
name: "foobar",
start: "2016-12-13 07:00:00",
start_date: false,
stop: "2016-12-13 15:00:00",
stop_date: false,
});
}
if (method === "write") {
assert.deepEqual(args[1], {
allday: false,
start: "2016-12-13 07:00:00",
stop: "2016-12-14 07:00:00",
});
}
},
});
await selectTimeRange(target, "2016-12-13 08:00:00", "2016-12-13 16:00:00");
await editInput(target, ".modal [name=name] input", "foobar");
await click(target, ".modal .o_form_button_save");
await resizeEventToTime(target, 8, "2016-12-14 08:00:00");
const event = findEvent(target, 8);
assert.strictEqual(event.textContent, "foobar");
assert.notOk(event.closest(".fc-day-grid"), "event should not be in the all day slots");
});
QUnit.test(`correctly display year view`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await toggleFilter(target, "partner_ids", 1);
await toggleFilter(target, "partner_ids", 2);
// Check view
assert.containsN(target, ".fc-month", 12);
assert.strictEqual(
target.querySelector(".fc-month .fc-header-toolbar").textContent,
"Jan 2016"
);
assert.containsN(
target,
".fc-bgevent",
7,
"There should be 6 events displayed but there is 1 split on 2 weeks"
);
assert.containsN(target, ".o_event_hatched", 3);
assert.containsOnce(target, ".o_event_striked");
await clickDate(target, "2016-11-17");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-11-16");
assert.containsOnce(target, ".o_popover");
let popoverText = target
.querySelector(".o_popover")
.textContent.replace(/\s{2,}/g, " ")
.trim();
assert.strictEqual(popoverText, "November 14-16, 2016event 7");
await click(target, ".o_cw_popover_close");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-11-14");
assert.containsOnce(target, ".o_popover");
popoverText = target
.querySelector(".o_popover")
.textContent.replace(/\s{2,}/g, " ")
.trim();
assert.strictEqual(popoverText, "November 14-16, 2016event 7");
await click(target, ".o_cw_popover_close");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-11-13");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-12-10");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-12-12");
assert.containsOnce(target, ".o_popover");
popoverText = target
.querySelector(".o_popover")
.textContent.replace(/\s{2,}/g, " ")
.trim();
assert.strictEqual(popoverText, "December 12, 201611:55 event 216:55 event 3");
await click(target, ".o_cw_popover_close");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-12-14");
assert.containsOnce(target, ".o_popover");
popoverText = target
.querySelector(".o_popover")
.textContent.replace(/\s{2,}/g, " ")
.trim();
assert.strictEqual(popoverText, "December 14, 2016event 4December 13-20, 2016event 5");
await click(target, ".o_cw_popover_close");
assert.containsNone(target, ".o_popover");
await clickDate(target, "2016-12-21");
assert.containsNone(target, ".o_popover");
});
QUnit.test(`toggle filters in year view`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
function checkEvents(countMap) {
for (const [id, count] of Object.entries(countMap)) {
assert.containsN(target, `.fc-bgevent[data-event-id="${id}"]`, count);
}
}
// activate partner filter
await toggleFilter(target, "partner_ids", 1);
await toggleFilter(target, "partner_ids", 2);
checkEvents({ 1: 1, 2: 1, 3: 1, 4: 1, 5: 2, 7: 1 });
await toggleFilter(target, "partner_ids", 2);
checkEvents({ 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 7: 0 });
await toggleFilter(target, "partner_id", 1);
checkEvents({ 1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 7: 0 });
await toggleFilter(target, "partner_id", 4);
checkEvents({ 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 7: 0 });
await toggleFilter(target, "partner_ids", 1);
checkEvents({ 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 7: 0 });
await toggleFilter(target, "partner_ids", 2);
checkEvents({ 1: 1, 2: 1, 3: 0, 4: 0, 5: 2, 7: 1 });
await toggleFilter(target, "partner_id", 4);
checkEvents({ 1: 1, 2: 1, 3: 0, 4: 0, 5: 0, 7: 1 });
});
QUnit.test(`allowed scales`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await click(target, ".o_calendar_scale_buttons .scale_button_selection");
assert.containsOnce(target, ".o_calendar_scale_buttons .o_calendar_button_day");
assert.containsOnce(target, ".o_calendar_scale_buttons .o_calendar_button_week");
assert.containsNone(target, ".o_calendar_scale_buttons .o_calendar_button_month");
assert.containsNone(target, ".o_calendar_scale_buttons .o_calendar_button_year");
});
QUnit.test(`click outside the popup should close it`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsNone(target, ".o_cw_popover");
await clickEvent(target, 1);
assert.containsOnce(target, ".o_cw_popover", "open popup when click on event");
await click(target, ".o_cw_popover .o_cw_body");
assert.containsOnce(target, ".o_cw_popover", "keep popup openned when click inside popup");
await click(target);
assert.containsNone(target, ".o_cw_popover", "close popup when click outside popup");
});
QUnit.test(`fields are added in the right order in popover`, async (assert) => {
const def = makeDeferred();
class DeferredWidget extends owl.Component {
setup() {
owl.onWillStart(() => def);
}
}
DeferredWidget.template = owl.xml``;
fieldRegistry.add("deferred_widget", DeferredWidget);
registerCleanup(() => fieldRegistry.remove("deferred_widget"));
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
assert.containsNone(target, ".o_cw_popover");
def.resolve();
await nextTick();
assert.containsOnce(target, ".o_cw_popover");
assert.strictEqual(
target.querySelector(".o_cw_popover .o_cw_popover_fields_secondary").textContent,
"user: name: event 4"
);
});
QUnit.test(`select events and discard create`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
assert.containsN(target, ".fc-dayGridMonth-view", 12, "should display in year mode");
await selectDateRange(target, "2016-11-13", "2016-11-19");
assert.containsOnce(
target,
".o-calendar-quick-create",
"should open the form view in dialog when select multiple days"
);
assert.hasAttrValue(
target.querySelector(".fc-highlight"),
"colspan",
"7",
"should highlight 7 days"
);
await click(target, ".o-calendar-quick-create--cancel-btn");
assert.containsNone(target, ".fc-highlight", "should not highlight days");
});
QUnit.test(`create event in year view`, async (assert) => {
assert.expect(6);
let expectedEvent;
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
mockRPC(route, { method, args }) {
if (method === "create") {
assert.deepEqual(args[0], expectedEvent);
}
},
});
// Select the whole month of July
expectedEvent = {
allday: true,
start: "2016-07-01",
stop: "2016-07-31",
name: "Whole July",
};
await selectDateRange(target, "2016-07-01", "2016-07-31");
await editInput(target, ".o-calendar-quick-create--input[name=title]", "Whole July");
await click(target, ".o-calendar-quick-create--create-btn");
// get all rows for event 8
assert.containsN(target, ".o_event[data-event-id='8']", 6);
assert.deepEqual(
[...target.querySelectorAll(".o_event[data-event-id='8']")].map((cell) => cell.colSpan),
[2, 7, 7, 7, 7, 1],
"rows should highlight multiple days"
);
// Select the whole month of November
expectedEvent = {
allday: true,
start: "2016-11-01",
stop: "2016-11-30",
name: "Whole November",
};
await selectDateRange(target, "2016-11-01", "2016-11-30");
await editInput(target, ".o-calendar-quick-create--input[name=title]", "Whole November");
await click(target, ".o-calendar-quick-create--create-btn");
// get all rows for event 9
assert.containsN(target, ".o_event[data-event-id='9']", 5);
assert.deepEqual(
[...target.querySelectorAll(".o_event[data-event-id='9']")].map((cell) => cell.colSpan),
[5, 7, 7, 7, 4],
"rows should highlight multiple days"
);
});
QUnit.test(`popover ignores readonly field modifier`, async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 4);
// test would fail here if we don't ignore readonly modifier
assert.containsOnce(target, ".o_cw_popover");
});
QUnit.test("can not select invalid scale from datepicker", async (assert) => {
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await click(target, ".ui-datepicker-today");
// test would fail here if we went to week mode
assert.containsOnce(target, ".fc-dayGridMonth-view");
});
QUnit.test(
"sample data are not removed when switching back from calendar view",
async function (assert) {
serverData.models.event.records = [];
serverData.actions = {
1: {
id: 1,
name: "Partners",
res_model: "event",
type: "ir.actions.act_window",
views: [
[false, "list"],
[false, "calendar"],
],
},
};
serverData.views = {
"event,false,calendar": ``,
"event,false,list": `
`,
"event,false,search": ``,
};
const webClient = await createWebClient({
serverData,
async mockRPC(route, args) {
if (args.method === "check_access_rights") {
return true;
}
if (route.endsWith("/has_group")) {
return true;
}
},
});
await doAction(webClient, 1);
assert.containsOnce(target, ".o_list_view", "should have rendered a list view");
assert.containsOnce(target, ".o_view_sample_data", "should have sample data");
await click(target, ".o_cp_switch_buttons .o_calendar");
assert.containsOnce(
target,
".o_calendar_container",
"should have rendered a calendar view"
);
await click(target, ".o_cp_switch_buttons .o_list");
assert.containsOnce(target, ".o_list_view", "should have rendered a list view");
assert.containsOnce(target, ".o_view_sample_data", "should have sample data");
}
);
QUnit.test("html field on calendar shouldn't have a tooltip", async (assert) => {
serverData.models.event.fields.description = { string: "Description", type: "html" };
serverData.models.event.records.push({
id: 8,
user_id: uid,
partner_id: 1,
name: "event with html",
start: "2016-12-12 12:00:00",
stop: "2016-12-12 12:00:00",
allday: false,
partner_ids: [1, 2, 3],
type: 1,
is_hatched: false,
description: "description
",
});
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
`,
});
await clickEvent(target, 8);
const descriptionField = target.querySelector(
'.o_cw_popover_field .o_field_widget[name="description"]'
);
const parentLi = descriptionField.closest("li");
assert.strictEqual(parentLi.getAttribute("data-tooltip"), "");
});
});