Skip to content

Commit 5dd4937

Browse files
chore: add e2e test for ai translation (calcom#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>
1 parent 866a0e2 commit 5dd4937

9 files changed

Lines changed: 106 additions & 44 deletions

File tree

apps/web/playwright/event-types.e2e.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { randomString } from "@calcom/lib/random";
77
import { test } from "./lib/fixtures";
88
import {
99
bookTimeSlot,
10-
createNewEventType,
10+
createNewUserEventType,
1111
gotoBookingPage,
1212
gotoFirstEventType,
1313
saveEventType,
@@ -55,15 +55,15 @@ test.describe("Event Types tests", () => {
5555
test("can add new event type", async ({ page }) => {
5656
const nonce = randomString(3);
5757
const eventTitle = `hello ${nonce}`;
58-
await createNewEventType(page, { eventTitle });
58+
await createNewUserEventType(page, { eventTitle });
5959
await page.goto("/event-types");
6060
await expect(page.locator(`text='${eventTitle}'`)).toBeVisible();
6161
});
6262

6363
test("new event type appears first in the list", async ({ page }) => {
6464
const nonce = randomString(3);
6565
const eventTitle = `hello ${nonce}`;
66-
await createNewEventType(page, { eventTitle });
66+
await createNewUserEventType(page, { eventTitle });
6767
await page.goto("/event-types");
6868
const firstEvent = page.locator("[data-testid=event-types] > li a").first();
6969
const firstEventTitle = await firstEvent.getAttribute("title");
@@ -73,7 +73,7 @@ test.describe("Event Types tests", () => {
7373
test("enabling recurring event comes with default options", async ({ page }) => {
7474
const nonce = randomString(3);
7575
const eventTitle = `my recurring event ${nonce}`;
76-
await createNewEventType(page, { eventTitle });
76+
await createNewUserEventType(page, { eventTitle });
7777

7878
// fix the race condition
7979
await page.waitForSelector('[data-testid="event-title"]');
@@ -393,7 +393,7 @@ test.describe("Event Types tests", () => {
393393
}) => {
394394
const nonce = randomString(3);
395395
const eventTitle = `Conflict event ${nonce}`;
396-
await createNewEventType(page, { eventTitle });
396+
await createNewUserEventType(page, { eventTitle });
397397
await page.goto("/event-types");
398398
await page.click(`text=${eventTitle}`);
399399

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { expect } from "@playwright/test";
2+
import { loginUser } from "playwright/fixtures/regularBookings";
3+
4+
import { MembershipRole } from "@calcom/prisma/enums";
5+
6+
import { test } from "../lib/fixtures";
7+
8+
test.describe.configure({ mode: "parallel" });
9+
10+
test.describe("AI Translation - Event Type", () => {
11+
test("Toggle should not be clickable for free plan members", async ({ page, users, bookingPage }) => {
12+
await loginUser(users);
13+
await page.goto("/event-types");
14+
await bookingPage.goToEventType("30 min");
15+
expect(await bookingPage.getAITranslationToggleDisabled()).toBe(true);
16+
});
17+
18+
test("Toggle should not be clickable for team plan members", async ({ page, users, bookingPage }) => {
19+
const user = await users.create(
20+
{ name: "testuser" },
21+
{
22+
hasTeam: true,
23+
teamRole: MembershipRole.MEMBER,
24+
}
25+
);
26+
await user.apiLogin();
27+
await page.goto("/event-types");
28+
await bookingPage.goToEventType("30 min");
29+
expect(await bookingPage.getAITranslationToggleDisabled()).toBe(true);
30+
});
31+
32+
test("Toggle should be clickable for org members", async ({ page, orgs, users, bookingPage }) => {
33+
const org = await orgs.create({
34+
name: "acme",
35+
});
36+
const user = await users.create({
37+
organizationId: org.id,
38+
roleInOrganization: MembershipRole.MEMBER,
39+
});
40+
await user.apiLogin();
41+
await page.goto("/event-types");
42+
await bookingPage.goToEventType("30 min");
43+
expect(await bookingPage.getAITranslationToggleDisabled()).toBe(false);
44+
});
45+
});

apps/web/playwright/fixtures/regularBookings.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,19 @@ export function createBookingPageFixture(page: Page) {
198198
checkTimeSlotsCount: async (eventTypePage: Page, count: number) => {
199199
await expect(eventTypePage.getByTestId("time")).toHaveCount(count);
200200
},
201+
getAITranslationToggleDisabled: async () => {
202+
await page.waitForSelector('[data-testid="ai_translation_toggle"]', {
203+
timeout: 5000,
204+
state: "attached",
205+
});
206+
const toggle = page.getByTestId("ai_translation_toggle");
207+
return (await toggle.getAttribute("disabled")) !== null;
208+
},
209+
toggleAITranslation: async () => {
210+
await page.getByTestId("ai_translation_toggle").click();
211+
},
212+
updateEventTypeDescription: async (description: string) => {
213+
await page.getByTestId("editor-input").fill(description);
214+
},
201215
};
202216
}

apps/web/playwright/lib/testUtils.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { createServer } from "http";
77
// eslint-disable-next-line no-restricted-imports
88
import type { Messages } from "mailhog";
99
import { totp } from "otplib";
10+
import { v4 as uuid } from "uuid";
1011

1112
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
1213
import type { Prisma } from "@calcom/prisma/client";
1314
import { BookingStatus, SchedulingType } from "@calcom/prisma/enums";
1415

1516
import type { createEmailsFixture } from "../fixtures/emails";
17+
import type { CreateUsersFixture } from "../fixtures/users";
1618
import type { Fixtures } from "./fixtures";
1719

1820
type Request = IncomingMessage & { body?: unknown };
@@ -177,10 +179,12 @@ export async function expectSlotNotAllowedToBook(page: Page) {
177179
await expect(page.locator("[data-testid=slot-not-allowed-to-book]")).toBeVisible();
178180
}
179181

180-
export const createNewEventType = async (page: Page, args: { eventTitle: string }) => {
182+
export const createNewUserEventType = async (page: Page, args: { eventTitle: string; username?: string }) => {
181183
await page.click("[data-testid=new-event-type]");
182-
const eventTitle = args.eventTitle;
183-
await page.fill("[name=title]", eventTitle);
184+
if (args.username) {
185+
await page.getByRole("button", { name: args.username }).click();
186+
}
187+
await page.fill("[name=title]", args.eventTitle);
184188
await page.fill("[name=length]", "10");
185189
await page.click("[type=submit]");
186190

@@ -216,7 +220,7 @@ export async function setupManagedEvent({
216220

217221
export const createNewSeatedEventType = async (page: Page, args: { eventTitle: string }) => {
218222
const eventTitle = args.eventTitle;
219-
await createNewEventType(page, { eventTitle });
223+
await createNewUserEventType(page, { eventTitle });
220224
await page.waitForSelector('[data-testid="event-title"]');
221225
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toHaveAttribute(
222226
"aria-current",
@@ -563,3 +567,26 @@ export async function expectPageToBeNotFound({ page, url }: { page: Page; url: s
563567
await page.goto(`${url}`);
564568
await expect(page.getByTestId(`404-page`)).toBeVisible();
565569
}
570+
571+
export async function setupOrgMember(users: CreateUsersFixture) {
572+
const orgRequestedSlug = `example-${uuid()}`;
573+
574+
const orgMember = await users.create(undefined, {
575+
hasTeam: true,
576+
isOrg: true,
577+
hasSubteam: true,
578+
isOrgVerified: true,
579+
isDnsSetup: true,
580+
orgRequestedSlug,
581+
schedulingType: SchedulingType.ROUND_ROBIN,
582+
});
583+
584+
const { team: org } = await orgMember.getOrgMembership();
585+
const { team } = await orgMember.getFirstTeamMembership();
586+
const teamEvent = await orgMember.getFirstTeamEvent(team.id);
587+
const userEvent = orgMember.eventTypes[0];
588+
589+
await orgMember.apiLogin();
590+
591+
return { orgMember, org, team, teamEvent, userEvent };
592+
}

apps/web/playwright/manage-booking-questions.e2e.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { CalendarEvent } from "@calcom/types/Calendar";
1212
import { test } from "./lib/fixtures";
1313
import {
1414
createHttpServer,
15-
createNewEventType,
15+
createNewUserEventType,
1616
selectFirstAvailableTimeSlotNextMonth,
1717
submitAndWaitForResponse,
1818
} from "./lib/testUtils";
@@ -752,7 +752,7 @@ test.describe("Text area min and max characters text", () => {
752752

753753
// We wait until loading is finished
754754
await page.waitForSelector('[data-testid="event-types"]');
755-
await createNewEventType(page, { eventTitle });
755+
await createNewUserEventType(page, { eventTitle });
756756
await page.waitForSelector('[data-testid="event-title"]');
757757
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toContainText("Event Setup"); //fix the race condition
758758
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toHaveAttribute(

apps/web/playwright/organization/organization-settings.e2e.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,9 @@
11
import { expect } from "@playwright/test";
22
import type { Page } from "@playwright/test";
3-
import { doOnOrgDomain } from "playwright/lib/testUtils";
4-
import { v4 as uuid } from "uuid";
3+
import { doOnOrgDomain, setupOrgMember } from "playwright/lib/testUtils";
54

6-
import { SchedulingType } from "@calcom/prisma/enums";
7-
8-
import type { CreateUsersFixture } from "../fixtures/users";
95
import { test } from "../lib/fixtures";
106

11-
async function setupOrgMember(users: CreateUsersFixture) {
12-
const orgRequestedSlug = `example-${uuid()}`;
13-
14-
const orgMember = await users.create(undefined, {
15-
hasTeam: true,
16-
isOrg: true,
17-
hasSubteam: true,
18-
isOrgVerified: true,
19-
isDnsSetup: true,
20-
orgRequestedSlug,
21-
schedulingType: SchedulingType.ROUND_ROBIN,
22-
});
23-
24-
const { team: org } = await orgMember.getOrgMembership();
25-
const { team } = await orgMember.getFirstTeamMembership();
26-
const teamEvent = await orgMember.getFirstTeamEvent(team.id);
27-
const userEvent = orgMember.eventTypes[0];
28-
29-
await orgMember.apiLogin();
30-
31-
return { orgMember, org, team, teamEvent, userEvent };
32-
}
33-
347
type TestContext = Awaited<ReturnType<typeof setupOrgMember>>;
358

369
interface ToggleSeoSwitchParams {

packages/features/bookings/Booker/components/EventMeta.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ export const EventMeta = ({
168168
{translatedTitle ?? event?.title}
169169
</EventTitle>
170170
{(event.description || translatedDescription) && (
171-
<EventMetaBlock contentClassName="mb-8 break-words max-w-full max-h-[180px] scroll-bar pr-4">
171+
<EventMetaBlock
172+
data-testid="event-meta-description"
173+
contentClassName="mb-8 break-words max-w-full max-h-[180px] scroll-bar pr-4">
172174
<div
173175
// eslint-disable-next-line react/no-danger
174176
dangerouslySetInnerHTML={{

packages/features/bookings/components/event-meta/Details.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ type EventDetailCustomBlock = {
4343

4444
type EventDetailsProps = EventDetailsPropsBase & (EventDetailDefaultBlock | EventDetailCustomBlock);
4545

46-
interface EventMetaProps {
46+
interface EventMetaProps extends React.HTMLAttributes<HTMLDivElement> {
4747
customIcon?: React.ReactNode;
4848
icon?: IconName;
4949
iconUrl?: string;
50-
children: React.ReactNode;
5150
// Emphasises the text in the block. For now only
5251
// applying in dark mode.
5352
highlight?: boolean;
5453
contentClassName?: string;
55-
className?: string;
5654
isDark?: boolean;
5755
}
5856

@@ -80,6 +78,7 @@ export const EventMetaBlock = ({
8078
contentClassName,
8179
className,
8280
isDark,
81+
...rest
8382
}: EventMetaProps) => {
8483
if (!React.Children.count(children)) return null;
8584

@@ -89,7 +88,8 @@ export const EventMetaBlock = ({
8988
"flex items-start justify-start text-sm",
9089
highlight ? "text-emphasis" : "text-text",
9190
className
92-
)}>
91+
)}
92+
{...rest}>
9393
{iconUrl ? (
9494
<img
9595
src={iconUrl}

packages/features/eventtypes/components/tabs/setup/EventSetupTab.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export const EventSetupTab = (
170170
}}
171171
disabled={!orgId}
172172
tooltip={!orgId ? t("orgs_upgrade_to_enable_feature") : undefined}
173+
data-testid="ai_translation_toggle"
173174
/>
174175
</div>
175176
)}

0 commit comments

Comments
 (0)