chore: add e2e test for ai translation (#18061)
* make createNewEventType receive description arg * remove @test tag * wip * wip2 * remove @test tag * modify EventMetaProps * add test id event-meta-description * improve test * better name * try * finalized * fix * fix type check * address comment * cleann up * cleann up * fix * Update ai-translation.e2e.ts * fix * simplify --------- Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
1 parent
866a0e2596
commit
5dd4937a1a
9 files changed
+106
-44
No files matched your search
@@ -7,7 +7,7 @@ import { randomString } from "@calcom/lib/random";
|
||||
import { test } from "./lib/fixtures";
|
||||
import {
|
||||
bookTimeSlot,
|
||||
createNewEventType,
|
||||
createNewUserEventType,
|
||||
gotoBookingPage,
|
||||
gotoFirstEventType,
|
||||
saveEventType,
|
||||
@@ -55,7 +55,7 @@ test.describe("Event Types tests", () => {
|
||||
test("can add new event type", async ({ page }) => {
|
||||
const nonce = randomString(3);
|
||||
const eventTitle = `hello ${nonce}`;
|
||||
await createNewEventType(page, { eventTitle });
|
||||
await createNewUserEventType(page, { eventTitle });
|
||||
await page.goto("/event-types");
|
||||
await expect(page.locator(`text='${eventTitle}'`)).toBeVisible();
|
||||
});
|
||||
@@ -63,7 +63,7 @@ test.describe("Event Types tests", () => {
|
||||
test("new event type appears first in the list", async ({ page }) => {
|
||||
const nonce = randomString(3);
|
||||
const eventTitle = `hello ${nonce}`;
|
||||
await createNewEventType(page, { eventTitle });
|
||||
await createNewUserEventType(page, { eventTitle });
|
||||
await page.goto("/event-types");
|
||||
const firstEvent = page.locator("[data-testid=event-types] > li a").first();
|
||||
const firstEventTitle = await firstEvent.getAttribute("title");
|
||||
@@ -73,7 +73,7 @@ test.describe("Event Types tests", () => {
|
||||
test("enabling recurring event comes with default options", async ({ page }) => {
|
||||
const nonce = randomString(3);
|
||||
const eventTitle = `my recurring event ${nonce}`;
|
||||
await createNewEventType(page, { eventTitle });
|
||||
await createNewUserEventType(page, { eventTitle });
|
||||
|
||||
// fix the race condition
|
||||
await page.waitForSelector('[data-testid="event-title"]');
|
||||
@@ -393,7 +393,7 @@ test.describe("Event Types tests", () => {
|
||||
}) => {
|
||||
const nonce = randomString(3);
|
||||
const eventTitle = `Conflict event ${nonce}`;
|
||||
await createNewEventType(page, { eventTitle });
|
||||
await createNewUserEventType(page, { eventTitle });
|
||||
await page.goto("/event-types");
|
||||
await page.click(`text=${eventTitle}`);
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { expect } from "@playwright/test";
|
||||
import { loginUser } from "playwright/fixtures/regularBookings";
|
||||
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { test } from "../lib/fixtures";
|
||||
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
||||
test.describe("AI Translation - Event Type", () => {
|
||||
test("Toggle should not be clickable for free plan members", async ({ page, users, bookingPage }) => {
|
||||
await loginUser(users);
|
||||
await page.goto("/event-types");
|
||||
await bookingPage.goToEventType("30 min");
|
||||
expect(await bookingPage.getAITranslationToggleDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
test("Toggle should not be clickable for team plan members", async ({ page, users, bookingPage }) => {
|
||||
const user = await users.create(
|
||||
{ name: "testuser" },
|
||||
{
|
||||
hasTeam: true,
|
||||
teamRole: MembershipRole.MEMBER,
|
||||
}
|
||||
);
|
||||
await user.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
await bookingPage.goToEventType("30 min");
|
||||
expect(await bookingPage.getAITranslationToggleDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
test("Toggle should be clickable for org members", async ({ page, orgs, users, bookingPage }) => {
|
||||
const org = await orgs.create({
|
||||
name: "acme",
|
||||
});
|
||||
const user = await users.create({
|
||||
organizationId: org.id,
|
||||
roleInOrganization: MembershipRole.MEMBER,
|
||||
});
|
||||
await user.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
await bookingPage.goToEventType("30 min");
|
||||
expect(await bookingPage.getAITranslationToggleDisabled()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -198,5 +198,19 @@ export function createBookingPageFixture(page: Page) {
|
||||
checkTimeSlotsCount: async (eventTypePage: Page, count: number) => {
|
||||
await expect(eventTypePage.getByTestId("time")).toHaveCount(count);
|
||||
},
|
||||
getAITranslationToggleDisabled: async () => {
|
||||
await page.waitForSelector('[data-testid="ai_translation_toggle"]', {
|
||||
timeout: 5000,
|
||||
state: "attached",
|
||||
});
|
||||
const toggle = page.getByTestId("ai_translation_toggle");
|
||||
return (await toggle.getAttribute("disabled")) !== null;
|
||||
},
|
||||
toggleAITranslation: async () => {
|
||||
await page.getByTestId("ai_translation_toggle").click();
|
||||
},
|
||||
updateEventTypeDescription: async (description: string) => {
|
||||
await page.getByTestId("editor-input").fill(description);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -7,12 +7,14 @@ import { createServer } from "http";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import type { Messages } from "mailhog";
|
||||
import { totp } from "otplib";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import { BookingStatus, SchedulingType } from "@calcom/prisma/enums";
|
||||
|
||||
import type { createEmailsFixture } from "../fixtures/emails";
|
||||
import type { CreateUsersFixture } from "../fixtures/users";
|
||||
import type { Fixtures } from "./fixtures";
|
||||
|
||||
type Request = IncomingMessage & { body?: unknown };
|
||||
@@ -177,10 +179,12 @@ export async function expectSlotNotAllowedToBook(page: Page) {
|
||||
await expect(page.locator("[data-testid=slot-not-allowed-to-book]")).toBeVisible();
|
||||
}
|
||||
|
||||
export const createNewEventType = async (page: Page, args: { eventTitle: string }) => {
|
||||
export const createNewUserEventType = async (page: Page, args: { eventTitle: string; username?: string }) => {
|
||||
await page.click("[data-testid=new-event-type]");
|
||||
const eventTitle = args.eventTitle;
|
||||
await page.fill("[name=title]", eventTitle);
|
||||
if (args.username) {
|
||||
await page.getByRole("button", { name: args.username }).click();
|
||||
}
|
||||
await page.fill("[name=title]", args.eventTitle);
|
||||
await page.fill("[name=length]", "10");
|
||||
await page.click("[type=submit]");
|
||||
|
||||
@@ -216,7 +220,7 @@ export async function setupManagedEvent({
|
||||
|
||||
export const createNewSeatedEventType = async (page: Page, args: { eventTitle: string }) => {
|
||||
const eventTitle = args.eventTitle;
|
||||
await createNewEventType(page, { eventTitle });
|
||||
await createNewUserEventType(page, { eventTitle });
|
||||
await page.waitForSelector('[data-testid="event-title"]');
|
||||
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toHaveAttribute(
|
||||
"aria-current",
|
||||
@@ -563,3 +567,26 @@ export async function expectPageToBeNotFound({ page, url }: { page: Page; url: s
|
||||
await page.goto(`${url}`);
|
||||
await expect(page.getByTestId(`404-page`)).toBeVisible();
|
||||
}
|
||||
|
||||
export async function setupOrgMember(users: CreateUsersFixture) {
|
||||
const orgRequestedSlug = `example-${uuid()}`;
|
||||
|
||||
const orgMember = await users.create(undefined, {
|
||||
hasTeam: true,
|
||||
isOrg: true,
|
||||
hasSubteam: true,
|
||||
isOrgVerified: true,
|
||||
isDnsSetup: true,
|
||||
orgRequestedSlug,
|
||||
schedulingType: SchedulingType.ROUND_ROBIN,
|
||||
});
|
||||
|
||||
const { team: org } = await orgMember.getOrgMembership();
|
||||
const { team } = await orgMember.getFirstTeamMembership();
|
||||
const teamEvent = await orgMember.getFirstTeamEvent(team.id);
|
||||
const userEvent = orgMember.eventTypes[0];
|
||||
|
||||
await orgMember.apiLogin();
|
||||
|
||||
return { orgMember, org, team, teamEvent, userEvent };
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
import { test } from "./lib/fixtures";
|
||||
import {
|
||||
createHttpServer,
|
||||
createNewEventType,
|
||||
createNewUserEventType,
|
||||
selectFirstAvailableTimeSlotNextMonth,
|
||||
submitAndWaitForResponse,
|
||||
} from "./lib/testUtils";
|
||||
@@ -752,7 +752,7 @@ test.describe("Text area min and max characters text", () => {
|
||||
|
||||
// We wait until loading is finished
|
||||
await page.waitForSelector('[data-testid="event-types"]');
|
||||
await createNewEventType(page, { eventTitle });
|
||||
await createNewUserEventType(page, { eventTitle });
|
||||
await page.waitForSelector('[data-testid="event-title"]');
|
||||
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toContainText("Event Setup"); //fix the race condition
|
||||
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toHaveAttribute(
|
||||
|
||||
@@ -1,36 +1,9 @@
|
||||
import { expect } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
import { doOnOrgDomain } from "playwright/lib/testUtils";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { doOnOrgDomain, setupOrgMember } from "playwright/lib/testUtils";
|
||||
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
|
||||
import type { CreateUsersFixture } from "../fixtures/users";
|
||||
import { test } from "../lib/fixtures";
|
||||
|
||||
async function setupOrgMember(users: CreateUsersFixture) {
|
||||
const orgRequestedSlug = `example-${uuid()}`;
|
||||
|
||||
const orgMember = await users.create(undefined, {
|
||||
hasTeam: true,
|
||||
isOrg: true,
|
||||
hasSubteam: true,
|
||||
isOrgVerified: true,
|
||||
isDnsSetup: true,
|
||||
orgRequestedSlug,
|
||||
schedulingType: SchedulingType.ROUND_ROBIN,
|
||||
});
|
||||
|
||||
const { team: org } = await orgMember.getOrgMembership();
|
||||
const { team } = await orgMember.getFirstTeamMembership();
|
||||
const teamEvent = await orgMember.getFirstTeamEvent(team.id);
|
||||
const userEvent = orgMember.eventTypes[0];
|
||||
|
||||
await orgMember.apiLogin();
|
||||
|
||||
return { orgMember, org, team, teamEvent, userEvent };
|
||||
}
|
||||
|
||||
type TestContext = Awaited<ReturnType<typeof setupOrgMember>>;
|
||||
|
||||
interface ToggleSeoSwitchParams {
|
||||
|
||||
@@ -168,7 +168,9 @@ export const EventMeta = ({
|
||||
{translatedTitle ?? event?.title}
|
||||
</EventTitle>
|
||||
{(event.description || translatedDescription) && (
|
||||
<EventMetaBlock contentClassName="mb-8 break-words max-w-full max-h-[180px] scroll-bar pr-4">
|
||||
<EventMetaBlock
|
||||
data-testid="event-meta-description"
|
||||
contentClassName="mb-8 break-words max-w-full max-h-[180px] scroll-bar pr-4">
|
||||
<div
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{
|
||||
|
||||
@@ -43,16 +43,14 @@ type EventDetailCustomBlock = {
|
||||
|
||||
type EventDetailsProps = EventDetailsPropsBase & (EventDetailDefaultBlock | EventDetailCustomBlock);
|
||||
|
||||
interface EventMetaProps {
|
||||
interface EventMetaProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
customIcon?: React.ReactNode;
|
||||
icon?: IconName;
|
||||
iconUrl?: string;
|
||||
children: React.ReactNode;
|
||||
// Emphasises the text in the block. For now only
|
||||
// applying in dark mode.
|
||||
highlight?: boolean;
|
||||
contentClassName?: string;
|
||||
className?: string;
|
||||
isDark?: boolean;
|
||||
}
|
||||
|
||||
@@ -80,6 +78,7 @@ export const EventMetaBlock = ({
|
||||
contentClassName,
|
||||
className,
|
||||
isDark,
|
||||
...rest
|
||||
}: EventMetaProps) => {
|
||||
if (!React.Children.count(children)) return null;
|
||||
|
||||
@@ -89,7 +88,8 @@ export const EventMetaBlock = ({
|
||||
"flex items-start justify-start text-sm",
|
||||
highlight ? "text-emphasis" : "text-text",
|
||||
className
|
||||
)}>
|
||||
)}
|
||||
{...rest}>
|
||||
{iconUrl ? (
|
||||
<img
|
||||
src={iconUrl}
|
||||
|
||||
@@ -170,6 +170,7 @@ export const EventSetupTab = (
|
||||
}}
|
||||
disabled={!orgId}
|
||||
tooltip={!orgId ? t("orgs_upgrade_to_enable_feature") : undefined}
|
||||
data-testid="ai_translation_toggle"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user