import { expect, test } from "@odoo/hoot"; import { press } from "@odoo/hoot-dom"; import { advanceTime, freezeTime, mockDate, runAllTimers } from "@odoo/hoot-mock"; import { defineRoomModels, mountRoomBookingView } from "@room/../tests/room_test_helpers"; import { asyncStep, contains, onRpc, waitForSteps } from "@web/../tests/web_test_helpers"; import { INACTIVITY_TIMEOUT, REFRESH_INTERVAL, } from "@room/room_booking/room_booking_view/room_booking_view"; const { DateTime, Duration } = luxon; function assertDisplayedTime(expectedTime) { expect(".o_room_top").toHaveText(DateTime.fromSQL(expectedTime).toFormat("T\nDDDD")); } /** * Assert that the room status is the expected one (right background color, right "busy" or "free" * status (icon), expected remaining time if there is an ongoing booking, and correct number of * bookings in the sidebar) * * @param {Object | false} remainingTime: remaining time of the current booking or false if no booking * @param {number} nbBookings: number of bookings in the sidebar */ function assertRoomStatus(remainingTime, nbBookings) { if (remainingTime) { const time = Duration.fromObject(remainingTime).toFormat("hh:mm:ss"); // Ignore second digits as it will be affected by mocked time being advanced expect(".o_room_remaining_time").toHaveText(new RegExp(`^${time.slice(0, -2)}\\d\\d$`)); expect(".o_room_booking_main > div").toHaveStyle( "background-image: linear-gradient(#FF0000DD, #FF0000DD)", { inline: true } ); expect("i.fa-calendar-times-o.fa-3x").toHaveCount(1); } else { expect(".o_room_remaining_time").toHaveCount(0); expect(".o_room_booking_main > div").toHaveStyle( "background-image: linear-gradient(#00FF00DD, #00FF00DD)", { inline: true } ); expect("i.fa-check-circle.fa-3x").toHaveCount(1); } expect(".o_room_sidebar .list-group-item").toHaveCount(nbBookings); } const QUICK_BOOK_SEL = ".btn-dark i.fa-rocket"; defineRoomModels(); test("Room Booking View - no meeting scheduled", async () => { onRpc("/room/room_test/get_existing_bookings", () => []); mockDate("2023-06-17 13:00:00", 0); await mountRoomBookingView(); assertDisplayedTime("2023-06-17 13:00:00"); assertRoomStatus(false, 0); expect(".o_room_sidebar p.o_test_description").toHaveClass("text-danger"); }); test("Room Booking View - ongoing meeting", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 11:00:00", stop_datetime: "2023-06-17 12:00:00", }, ]); mockDate("2023-06-17 11:15:00", 0); await mountRoomBookingView(); assertDisplayedTime("2023-06-17 11:15:00"); assertRoomStatus({ minutes: 44, seconds: 59 }, 1); }); test("Room Booking View - Quick Booking", async () => { const buttonsSelector = "input[placeholder='Booking Name'] + div button"; let expectedCreateArgs; onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 11:00:00", stop_datetime: "2023-06-17 12:00:00", }, ]); onRpc("/room/room_test/booking/create", async (request) => { const { params: args } = await request.json(); expect(args).toEqual(expectedCreateArgs); return 10; }); onRpc("/room/room_test/booking/10/delete", async () => { await notifyView("booking/delete", [{ id: 10 }]); asyncStep("delete_booking"); return true; }); mockDate("2023-06-17 09:59:00", 0); const { notifyView } = await mountRoomBookingView(true); await contains(QUICK_BOOK_SEL).click(); await contains("input[placeholder='Booking Name']").edit("Meeting"); expect(buttonsSelector).toHaveCount(3); // Last button should book the room for 30 min, and name should have been reset to default expectedCreateArgs = { name: "Meeting", start_datetime: "2023-06-17 09:59:00", stop_datetime: "2023-06-17 10:59:00", }; await contains(`${buttonsSelector}:last-child`).click(); await contains(".o_room_sidebar .list-group-item:first-child .fa-trash").click(); await contains(".modal-footer button:contains('Delete')").click(); await runAllTimers(); await waitForSteps(["delete_booking"]); mockDate("2023-06-17 10:25:00", 0); await contains(QUICK_BOOK_SEL).click(); expect(buttonsSelector).toHaveCount(2); expectedCreateArgs = { name: "Public Booking", start_datetime: "2023-06-17 10:25:00", stop_datetime: "2023-06-17 10:55:00", }; await contains(`${buttonsSelector}:last-child`).click(); await contains(".o_room_sidebar .list-group-item:first-child .fa-trash").click(); await contains(".modal-footer button:contains('Delete')").click(); await waitForSteps(["delete_booking"]); mockDate("2023-06-17 10:43:00", 0); await contains(QUICK_BOOK_SEL).click(); expect(buttonsSelector).toHaveCount(1); expectedCreateArgs = { name: "Public Booking", start_datetime: "2023-06-17 10:43:00", stop_datetime: "2023-06-17 10:58:00", }; await contains(buttonsSelector).click(); await contains(".o_room_sidebar .list-group-item:first-child .fa-trash").click(); await contains(".modal-footer button:contains('Delete')").click(); await waitForSteps(["delete_booking"]); mockDate("2023-06-17 10:59:00", 0); await contains(QUICK_BOOK_SEL).click(); expect(buttonsSelector).toHaveCount(0); }); test("Room Booking View - Booking Started", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 11:00:00", stop_datetime: "2023-06-17 12:00:00", }, ]); mockDate("2023-06-17 10:59:00", 0); freezeTime(); await mountRoomBookingView(); assertRoomStatus(false, 1); mockDate("2023-06-17 11:00:00", 0); await advanceTime(REFRESH_INTERVAL); assertRoomStatus({ minutes: 59, seconds: 59 }, 1); // Make sure there is no error with the following intervals await runAllTimers(); }); test("Room Booking View - Booking Ended", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 10:00:00", stop_datetime: "2023-06-17 11:00:00", }, ]); mockDate("2023-06-17 10:59:59", 0); await mountRoomBookingView(); assertRoomStatus({ seconds: 0 }, 1); mockDate("2023-06-17 11:00:00", 0); await advanceTime(REFRESH_INTERVAL); assertRoomStatus(false, 0); // Make sure there is no error with the following intervals await runAllTimers(); }); test("Room Booking View - Day Change", async () => { freezeTime(); onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-18 10:00:00", stop_datetime: "2023-06-18 11:00:00", }, ]); mockDate("2023-06-17 23:59:59", 0); await mountRoomBookingView(); expect(".o_room_sidebar h6:first-of-type:contains('Today')").toHaveCount(1); expect(".o_room_sidebar h6").toHaveCount(2); mockDate("2023-06-18 00:00:00", 0); await advanceTime(REFRESH_INTERVAL); expect(".o_room_sidebar h6:first-of-type").toHaveText("Today"); expect(".o_room_sidebar h6").toHaveCount(1); }); test("Room Booking View - Inactivity reset", async () => { onRpc("/room/room_test/get_existing_bookings", () => []); mockDate("2023-06-17 10:00:00", 0); freezeTime(); await mountRoomBookingView(); await contains(QUICK_BOOK_SEL).click(); await advanceTime(INACTIVITY_TIMEOUT); await contains(QUICK_BOOK_SEL).click(); await advanceTime(INACTIVITY_TIMEOUT - 1); // Writing the title should reset the timeout await press("s"); await advanceTime(INACTIVITY_TIMEOUT - 1); expect(".fa-check-circle.fa-3x").toHaveCount(0); // Clicking anywhere should reset the timeout await contains(".o_room_booking_main").click(); await advanceTime(INACTIVITY_TIMEOUT - 1); expect(".fa-check-circle.fa-3x").toHaveCount(0); // Make sure timeout still occurs await advanceTime(1); expect(".fa-check-circle.fa-3x").toHaveCount(1); }); test("Room Booking View - Consecutive Bookings", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 10:00:00", stop_datetime: "2023-06-17 11:00:00", }, { id: 2, name: "Booking 2", start_datetime: "2023-06-17 11:00:00", stop_datetime: "2023-06-17 12:00:00", }, ]); freezeTime(); mockDate("2023-06-17 10:59:59", 0); await mountRoomBookingView(); assertRoomStatus({ seconds: 1 }, 2); mockDate("2023-06-17 11:00:00", 0); await advanceTime(REFRESH_INTERVAL); assertRoomStatus({ minutes: 59, seconds: 59 }, 1); // Make sure next interval does not remove current booking await advanceTime(REFRESH_INTERVAL); assertRoomStatus({ minutes: 59, seconds: 58 }, 1); }); test("Room Booking View - Receiving new booking through bus", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 10:00:00", stop_datetime: "2023-06-17 11:00:00", }, { id: 2, name: "Booking 2", start_datetime: "2023-06-17 14:00:00", stop_datetime: "2023-06-17 15:00:00", }, ]); mockDate("2023-06-17 08:30:00", 0); const { notifyView } = await mountRoomBookingView(true); assertRoomStatus(false, 2); await notifyView("booking/create", [ { id: 3, name: "Booking 3", start_datetime: "2023-06-17 13:00:00", stop_datetime: "2023-06-17 13:30:00", }, ]); assertRoomStatus(false, 3); expect(".o_room_sidebar .list-group-item:eq(1)").toHaveText(/Booking 3/); await notifyView("booking/create", [ { id: 4, name: "Booking 4", start_datetime: "2023-06-17 08:30:00", stop_datetime: "2023-06-17 09:00:00", }, ]); expect(".o_room_sidebar .list-group-item:eq(0)").toHaveText(/Booking 4/); assertRoomStatus({ minutes: 29, seconds: 59 }, 4); await notifyView("booking/create", [ { id: 5, name: "Booking 5", start_datetime: "2023-06-17 07:00:00", stop_datetime: "2023-06-17 07:30:00", }, ]); expect(".o_room_sidebar .list-group-item:contains('Booking 5')").toHaveCount(0); }); test("Room Booking View - Receiving booking update through bus", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 10:00:00", stop_datetime: "2023-06-17 11:00:00", }, ]); mockDate("2023-06-17 10:30:00", 0); const { notifyView } = await mountRoomBookingView(true); assertRoomStatus({ minutes: 29, seconds: 59 }, 1); await notifyView("booking/update", [ { id: 1, name: "Booking 1 rescheduled", start_datetime: "2023-06-17 11:30:00", stop_datetime: "2023-06-17 12:00:00", }, ]); assertRoomStatus(false, 1); expect(".o_room_sidebar .list-group-item").toHaveText(/Booking 1 rescheduled/s); await notifyView("booking/update", [ { id: 2, name: "Ended to Current", start_datetime: "2023-06-17 10:00:00", stop_datetime: "2023-06-17 11:00:00", }, ]); expect(".o_room_sidebar .list-group-item:eq(0)").toHaveText(/Ended to Current/); assertRoomStatus({ minutes: 29, seconds: 59 }, 2); await notifyView("booking/update", [ { id: 2, name: "Ended to Current", start_datetime: "2023-06-17 10:00:00", stop_datetime: "2023-06-17 11:30:00", }, ]); assertRoomStatus({ minutes: 59, seconds: 59 }, 2); await notifyView("booking/update", [ { id: 1, name: "Booking 1 in the past", start_datetime: "2023-06-17 09:00:00", stop_datetime: "2023-06-17 09:30:00", }, ]); await advanceTime(REFRESH_INTERVAL); assertRoomStatus({ minutes: 59, seconds: 59 }, 1); }); test("Room Booking View - Receiving booking deletion through bus", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 11:00:00", stop_datetime: "2023-06-17 12:00:00", }, { id: 2, name: "Booking 2", start_datetime: "2023-06-17 12:00:00", stop_datetime: "2023-06-17 13:00:00", }, ]); mockDate("2023-06-17 11:15:00", 0); const { notifyView } = await mountRoomBookingView(true); assertRoomStatus({ minutes: 44, seconds: 59 }, 2); await notifyView("booking/delete", [{ id: 1 }]); assertRoomStatus(false, 1); expect(".o_room_sidebar .list-group-item").toHaveText(/Booking 2/); await notifyView("booking/delete", [{ id: 2 }]); assertRoomStatus(false, 0); }); test("Room Booking View - Booking spanning several days", async () => { onRpc("/room/room_test/get_existing_bookings", () => [ { id: 1, name: "Booking 1", start_datetime: "2023-06-17 11:00:00", stop_datetime: "2023-06-19 11:00:00", }, ]); mockDate("2023-06-18 10:00:00", 0); await mountRoomBookingView(); assertDisplayedTime("2023-06-18 10:00:00"); assertRoomStatus({ hours: 24, minutes: 59, seconds: 59 }, 1); mockDate("2023-06-19 10:00:00", 0); await advanceTime(REFRESH_INTERVAL); assertDisplayedTime("2023-06-19 10:00:00"); assertRoomStatus({ minutes: 59, seconds: 59 }, 1); mockDate("2023-06-19 12:00:00", 0); await runAllTimers(); assertDisplayedTime("2023-06-19 12:00:00"); assertRoomStatus(false, 0); });