fix: Not able to create consecutive ooo (#17388)
* fix: able to create consecutive ooo * added e2e test * chore * update to allow overlapping ooo and test for same * refactor to reduce repeating code in tests * nit * added testcases for consecutive and reverse redirects * fix:duplicate OOO creation * prevent duplicate entries, also added test * chore * update after merges * correct datetype in prisma query * update to changes in rdp after merge --------- Co-authored-by: Prashant Varma <prashantvarma5083@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Frame, Page, Request as PlaywrightRequest } from "@playwright/test";
|
||||
import type { Frame, Locator, Page, Request as PlaywrightRequest } from "@playwright/test";
|
||||
import { expect } from "@playwright/test";
|
||||
import { createHash } from "crypto";
|
||||
import EventEmitter from "events";
|
||||
@@ -538,3 +538,20 @@ export async function expectPageToBeNotFound({ page, url }: { page: Page; url: s
|
||||
await page.goto(`${url}`);
|
||||
await expect(page.getByTestId(`404-page`)).toBeVisible();
|
||||
}
|
||||
|
||||
export async function clickUntilDialogVisible(
|
||||
dialogOpenButton: Locator,
|
||||
visibleLocatorOnDialog: Locator,
|
||||
retries = 3,
|
||||
delay = 500
|
||||
) {
|
||||
for (let i = 0; i < retries; i++) {
|
||||
await dialogOpenButton.click();
|
||||
try {
|
||||
await visibleLocatorOnDialog.waitFor({ state: "visible", timeout: delay });
|
||||
return;
|
||||
} catch {
|
||||
if (i === retries - 1) throw new Error("Dialog did not appear after multiple attempts.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { randomString } from "@calcom/lib/random";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
import { submitAndWaitForResponse } from "./lib/testUtils";
|
||||
import { submitAndWaitForResponse, localize, clickUntilDialogVisible } from "./lib/testUtils";
|
||||
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
test.afterEach(async ({ users }) => {
|
||||
@@ -295,6 +295,107 @@ test.describe("Out of office", () => {
|
||||
// send request
|
||||
await saveAndWaitForResponse(page, 409);
|
||||
});
|
||||
|
||||
test("User can create separate out of office entries for consecutive dates", async ({ page, users }) => {
|
||||
const user = await users.create({ name: "userOne" });
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
|
||||
const addOOOButton = await page.getByTestId("add_entry_ooo");
|
||||
const dateButton = await page.locator('[data-testid="date-range"]');
|
||||
|
||||
//Creates 2 OOO entries:
|
||||
//First OOO is created on Next month 1st - 3rd
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "1", "3");
|
||||
await expect(page.locator(`data-testid=table-redirect-n-a`).nth(0)).toBeVisible();
|
||||
|
||||
//Second OOO is created on Next month 4th - 6th
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "4", "6");
|
||||
await expect(page.locator(`data-testid=table-redirect-n-a`).nth(1)).toBeVisible();
|
||||
});
|
||||
|
||||
test("User can create consecutive reverse redirect OOOs", async ({ page, users }) => {
|
||||
const teamMatesObj = [{ name: "member-1" }, { name: "member-2" }];
|
||||
const owner = await users.create(
|
||||
{ name: "owner" },
|
||||
{
|
||||
hasTeam: true,
|
||||
isOrg: true,
|
||||
teammates: teamMatesObj,
|
||||
}
|
||||
);
|
||||
const member1User = users.get().find((user) => user.name === "member-1");
|
||||
|
||||
await owner.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
|
||||
const addOOOButton = await page.getByTestId("add_entry_ooo");
|
||||
const dateButton = await page.locator('[data-testid="date-range"]');
|
||||
|
||||
//As owner,OOO is created on Next month 1st - 3rd, forwarding to 'member-1'
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "1", "3", "member-1");
|
||||
await expect(
|
||||
page.locator(`data-testid=table-redirect-${member1User?.username ?? "n-a"}`).nth(0)
|
||||
).toBeVisible();
|
||||
|
||||
//As member1, OOO is created on Next month 4th - 5th, forwarding to 'owner'
|
||||
await member1User?.apiLogin();
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "4", "5", "owner");
|
||||
await expect(page.locator(`data-testid=table-redirect-${owner.username ?? "n-a"}`).nth(0)).toBeVisible();
|
||||
});
|
||||
|
||||
test("User cannot create infinite or overlapping reverse redirect OOOs", async ({ page, users }) => {
|
||||
const t = await localize("en");
|
||||
const teamMatesObj = [{ name: "member-1" }, { name: "member-2" }];
|
||||
const owner = await users.create(
|
||||
{ name: "owner" },
|
||||
{
|
||||
hasTeam: true,
|
||||
isOrg: true,
|
||||
teammates: teamMatesObj,
|
||||
}
|
||||
);
|
||||
const member1User = users.get().find((user) => user.name === "member-1");
|
||||
|
||||
await owner.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
|
||||
const addOOOButton = await page.getByTestId("add_entry_ooo");
|
||||
const dateButton = await page.locator('[data-testid="date-range"]');
|
||||
|
||||
//As owner,OOO is created on Next month 1st - 3rd, forwarding to 'member-1'
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "1", "3", "member-1");
|
||||
await expect(
|
||||
page.locator(`data-testid=table-redirect-${member1User?.username ?? "n-a"}`).nth(0)
|
||||
).toBeVisible();
|
||||
|
||||
//As member1, expect error while OOO is created on Next month 2nd - 5th, forwarding to 'owner'
|
||||
await member1User?.apiLogin();
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "2", "5", "owner", 400);
|
||||
await expect(page.locator(`text=${t("booking_redirect_infinite_not_allowed")}`)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
async function saveAndWaitForResponse(page: Page, expectedStatusCode = 200) {
|
||||
@@ -312,3 +413,26 @@ async function selectToAndFromDates(page: Page, fromDate: string, toDate: string
|
||||
await page.locator(`button[name="day"]:has-text("${fromDate}")`).nth(0).click();
|
||||
await page.locator(`button[name="day"]:has-text("${toDate}")`).nth(0).click();
|
||||
}
|
||||
|
||||
async function selectDateAndCreateOOO(
|
||||
page: Page,
|
||||
fromDate: string,
|
||||
toDate: string,
|
||||
redirectToUser?: string,
|
||||
expectedStatusCode = 200
|
||||
) {
|
||||
const t = await localize("en");
|
||||
await page.locator(`button[name="next-month"]`).click();
|
||||
await page.locator(`button[name="day"]:has-text("${fromDate}")`).nth(0).click();
|
||||
await page.locator(`button[name="day"]:has-text("${toDate}")`).nth(0).click();
|
||||
await page.locator(`text=${t("create_an_out_of_office")}`).click();
|
||||
await page.getByTestId("reason_select").click();
|
||||
await page.getByTestId("select-option-4").click();
|
||||
await page.getByTestId("notes_input").click();
|
||||
await page.getByTestId("notes_input").fill("Demo notes");
|
||||
if (redirectToUser) {
|
||||
await page.getByTestId("profile-redirect-switch").click();
|
||||
await page.locator(`text=${redirectToUser}`).click();
|
||||
}
|
||||
await saveAndWaitForResponse(page, expectedStatusCode);
|
||||
}
|
||||
|
||||
@@ -114,15 +114,7 @@ export default function CreateEventTypeDialog({
|
||||
return (
|
||||
<Dialog
|
||||
name="new"
|
||||
clearQueryParamsOnClose={[
|
||||
"eventPage",
|
||||
"type",
|
||||
"description",
|
||||
"title",
|
||||
"length",
|
||||
"slug",
|
||||
"locations",
|
||||
]}>
|
||||
clearQueryParamsOnClose={["eventPage", "type", "description", "title", "length", "slug", "locations"]}>
|
||||
<DialogContent
|
||||
type="creation"
|
||||
enableOverflow
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,4 @@
|
||||
import type { calendar_v3 } from "@googleapis/calendar";
|
||||
import type {
|
||||
BookingSeat,
|
||||
DestinationCalendar,
|
||||
@@ -5,7 +6,6 @@ import type {
|
||||
SelectedCalendar as _SelectedCalendar,
|
||||
} from "@prisma/client";
|
||||
import type { Dayjs } from "dayjs";
|
||||
import type { calendar_v3 } from "@googleapis/calendar";
|
||||
import type { Time } from "ical.js";
|
||||
import type { TFunction } from "next-i18next";
|
||||
import type z from "zod";
|
||||
|
||||
Reference in New Issue
Block a user