cleanup(team-org): drop playwright helpers and fixtures (#28954)
This commit is contained in:
@@ -1,177 +0,0 @@
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
/**
|
||||
* Example usage:
|
||||
*
|
||||
* // Basic usage with default assignments
|
||||
* await createAttributes({
|
||||
* orgId: 1,
|
||||
* attributes: [
|
||||
* {
|
||||
* name: "Department",
|
||||
* type: "SINGLE_SELECT",
|
||||
* options: ["Engineering", "Sales", "Marketing"]
|
||||
* }
|
||||
* ],
|
||||
* assignments: [
|
||||
* {
|
||||
* memberIndex: 0,
|
||||
* attributeValues: {
|
||||
* "Department": ["Engineering"]
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
* });
|
||||
*
|
||||
* // Create attributes without assignments
|
||||
* await createAttributes({
|
||||
* orgId: 1,
|
||||
* attributes: [
|
||||
* { name: "Bio", type: "TEXT" },
|
||||
* { name: "Years of Experience", type: "NUMBER" }
|
||||
* ]
|
||||
* });
|
||||
*/
|
||||
|
||||
interface AttributeConfig {
|
||||
name: string;
|
||||
type: "SINGLE_SELECT" | "MULTI_SELECT" | "TEXT" | "NUMBER";
|
||||
options?: string[];
|
||||
}
|
||||
|
||||
interface MemberAssignment {
|
||||
memberIndex: number;
|
||||
attributeValues: Record<string, string[]>; // attribute name -> selected values
|
||||
}
|
||||
|
||||
interface SeedAttributesConfig {
|
||||
orgId: number;
|
||||
attributes: AttributeConfig[];
|
||||
assignments?: MemberAssignment[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeds attributes for an organization with configurable types, values, and assignments
|
||||
* @param config - Configuration object containing orgId, attributes, and optional assignments
|
||||
* @returns Array of created attributes with their options
|
||||
*/
|
||||
export async function createAttributes(config: SeedAttributesConfig) {
|
||||
const { orgId, attributes: attributeConfigs, assignments = [] } = config;
|
||||
|
||||
console.log(`🎯 Seeding attributes for org ${orgId}`);
|
||||
|
||||
// Check if attributes already exist
|
||||
const existingAttributes = await prisma.attribute.findMany({
|
||||
where: {
|
||||
teamId: orgId,
|
||||
name: {
|
||||
in: attributeConfigs.map((attr) => attr.name),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingAttributes.length > 0) {
|
||||
console.log(`Skipping attributes seed, attributes already exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
const memberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
teamId: orgId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`🎯 Creating attributes for org ${orgId}`);
|
||||
|
||||
const attributes: { id: string; name: string; options: { id: string; value: string }[] }[] = [];
|
||||
|
||||
for (const attr of attributeConfigs) {
|
||||
const attribute = await prisma.attribute.create({
|
||||
data: {
|
||||
name: attr.name,
|
||||
slug: `org:${orgId}-${attr.name.toLowerCase().replace(/ /g, "-")}`,
|
||||
type: attr.type as "TEXT" | "NUMBER" | "SINGLE_SELECT" | "MULTI_SELECT",
|
||||
teamId: orgId,
|
||||
enabled: true,
|
||||
options: attr.options
|
||||
? {
|
||||
create: attr.options.map((opt) => ({
|
||||
value: opt,
|
||||
slug: opt.toLowerCase().replace(/ /g, "-"),
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
include: {
|
||||
options: true,
|
||||
},
|
||||
});
|
||||
|
||||
attributes.push({
|
||||
id: attribute.id,
|
||||
name: attribute.name,
|
||||
options: attribute.options.map((opt) => ({
|
||||
id: opt.id,
|
||||
value: opt.value,
|
||||
})),
|
||||
});
|
||||
|
||||
console.log(`\t📝 Created attribute: ${attr.name}`);
|
||||
|
||||
// Process assignments for this attribute
|
||||
for (const assignment of assignments) {
|
||||
const { memberIndex, attributeValues } = assignment;
|
||||
|
||||
// Check if this assignment has values for the current attribute
|
||||
const valuesForAttribute = attributeValues[attr.name];
|
||||
if (!valuesForAttribute || valuesForAttribute.length === 0) {
|
||||
console.log(
|
||||
`\t\t⏭️ Skipped ${attr.name} assignment for member ${memberIndex + 1} - no values specified`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the member at the specified index
|
||||
const member = memberships[memberIndex];
|
||||
if (!member) {
|
||||
console.log(`\t\t⚠️ Skipped ${attr.name} assignment - member at index ${memberIndex} not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the options for the specified values
|
||||
const selectedOptions = attribute.options.filter((opt) => valuesForAttribute.includes(opt.value));
|
||||
|
||||
if (selectedOptions.length === 0) {
|
||||
console.log(
|
||||
`\t\t⚠️ Skipped ${attr.name} assignment for user ${
|
||||
member.userId
|
||||
} - no matching options found for values: ${valuesForAttribute.join(", ")}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create assignments for each selected option
|
||||
for (const option of selectedOptions) {
|
||||
await prisma.attributeToUser.create({
|
||||
data: {
|
||||
memberId: member.id,
|
||||
attributeOptionId: option.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const assignedValues = selectedOptions.map((opt) => opt.value);
|
||||
console.log(
|
||||
`\t\t✅ Assigned ${attr.name} [${assignedValues.map((v) => `"${v}"`).join(", ")}] to user ${
|
||||
member.userId
|
||||
} (member ${memberIndex + 1})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
|
||||
/**
|
||||
* Creates a team event type and assigns all team members as hosts
|
||||
* @param teamId - The team ID
|
||||
* @param eventType - Event type data to create
|
||||
* @returns The created event type
|
||||
*/
|
||||
export async function createRoundRobinTeamEventType({
|
||||
teamId,
|
||||
eventType,
|
||||
}: {
|
||||
teamId: number;
|
||||
eventType: Prisma.EventTypeCreateInput;
|
||||
}) {
|
||||
const createdEventType = await prisma.eventType.create({
|
||||
data: {
|
||||
...eventType,
|
||||
team: {
|
||||
connect: {
|
||||
id: teamId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
team: true,
|
||||
},
|
||||
});
|
||||
|
||||
const teamMemberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
|
||||
// Add all team members as hosts (excluding the current user who's already added)
|
||||
for (const membership of teamMemberships) {
|
||||
await prisma.host.create({
|
||||
data: {
|
||||
userId: membership.userId,
|
||||
eventTypeId: createdEventType.id,
|
||||
isFixed: false, // ROUND_ROBIN scheduling type
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return createdEventType;
|
||||
}
|
||||
@@ -1,14 +1,40 @@
|
||||
import { expect } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
import type { createUsersFixture } from "./fixtures/users";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
|
||||
import type { Page } from "@playwright/test";
|
||||
import { expect } from "@playwright/test";
|
||||
import { JSDOM } from "jsdom";
|
||||
import type { Messages } from "mailhog";
|
||||
import type { createEmailsFixture } from "./fixtures/emails";
|
||||
import type { createUsersFixture } from "./fixtures/users";
|
||||
import { test } from "./lib/fixtures";
|
||||
import { getEmailsReceivedByUser, submitAndWaitForResponse } from "./lib/testUtils";
|
||||
import { expectInvitationEmailToBeReceived } from "./team/expects";
|
||||
|
||||
const expectInvitationEmailToBeReceived = async ({
|
||||
emails,
|
||||
userEmail,
|
||||
subject,
|
||||
returnLink,
|
||||
}: {
|
||||
emails: ReturnType<typeof createEmailsFixture>;
|
||||
userEmail: string;
|
||||
subject?: string | null;
|
||||
returnLink?: string;
|
||||
}) => {
|
||||
if (!emails) return null;
|
||||
|
||||
const receivedEmails = await getEmailsReceivedByUser({ emails, userEmail });
|
||||
expect(receivedEmails?.total).toBe(1);
|
||||
|
||||
const [firstReceivedEmail] = (receivedEmails as Messages).items;
|
||||
if (subject) {
|
||||
expect(firstReceivedEmail.subject).toBe(subject);
|
||||
}
|
||||
if (!returnLink) return null;
|
||||
|
||||
const dom = new JSDOM(firstReceivedEmail.html);
|
||||
const anchor = dom.window.document.querySelector(`a[href*="${returnLink}"]`);
|
||||
return anchor?.getAttribute("href") ?? null;
|
||||
};
|
||||
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
||||
@@ -170,12 +196,10 @@ test.describe("Update Profile", () => {
|
||||
});
|
||||
|
||||
const testEmailVerificationLink = async ({
|
||||
page,
|
||||
prisma,
|
||||
emails,
|
||||
secondaryEmail,
|
||||
}: {
|
||||
page: Page;
|
||||
prisma: PrismaClient;
|
||||
emails: ReturnType<typeof createEmailsFixture>;
|
||||
secondaryEmail: string;
|
||||
@@ -186,13 +210,12 @@ test.describe("Update Profile", () => {
|
||||
identifier: secondaryEmail,
|
||||
},
|
||||
});
|
||||
const inviteLink = await expectInvitationEmailToBeReceived(
|
||||
page,
|
||||
const inviteLink = await expectInvitationEmailToBeReceived({
|
||||
emails,
|
||||
secondaryEmail,
|
||||
"Verify your email address",
|
||||
"verify-email"
|
||||
);
|
||||
userEmail: secondaryEmail,
|
||||
subject: "Verify your email address",
|
||||
returnLink: "verify-email",
|
||||
});
|
||||
expect(inviteLink).toEqual(`${WEBAPP_URL}/api/auth/verify-email?token=${verificationToken?.token}`);
|
||||
});
|
||||
};
|
||||
@@ -236,13 +259,12 @@ test.describe("Update Profile", () => {
|
||||
identifier: secondaryEmail,
|
||||
},
|
||||
});
|
||||
const inviteLink = await expectInvitationEmailToBeReceived(
|
||||
page,
|
||||
const inviteLink = await expectInvitationEmailToBeReceived({
|
||||
emails,
|
||||
secondaryEmail,
|
||||
"Verify your email address",
|
||||
"verify-email"
|
||||
);
|
||||
userEmail: secondaryEmail,
|
||||
subject: "Verify your email address",
|
||||
returnLink: "verify-email",
|
||||
});
|
||||
expect(inviteLink?.endsWith(`/api/auth/verify-email?token=${verificationToken?.token}`)).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -385,7 +407,7 @@ test.describe("Update Profile", () => {
|
||||
await expect(page.locator("button[data-testid=resend-verify-email-button]")).toBeEnabled();
|
||||
await page.getByTestId("resend-verify-email-button").click();
|
||||
|
||||
await testEmailVerificationLink({ page, prisma, emails, secondaryEmail });
|
||||
await testEmailVerificationLink({ prisma, emails, secondaryEmail });
|
||||
|
||||
const verificationToken = await prisma.verificationToken.findFirst({
|
||||
where: {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { expect } from "@playwright/test";
|
||||
import { JSDOM } from "jsdom";
|
||||
import type { Messages } from "mailhog";
|
||||
import type { createEmailsFixture } from "playwright/fixtures/emails";
|
||||
|
||||
import { getEmailsReceivedByUser } from "../lib/testUtils";
|
||||
|
||||
export async function expectInvitationEmailToBeReceived(
|
||||
page: Page,
|
||||
emails: ReturnType<typeof createEmailsFixture>,
|
||||
userEmail: string,
|
||||
subject?: string | null,
|
||||
returnLink?: string
|
||||
) {
|
||||
if (!emails) return null;
|
||||
|
||||
const receivedEmails = await getEmailsReceivedByUser({ emails, userEmail });
|
||||
expect(receivedEmails?.total).toBe(1);
|
||||
|
||||
const [firstReceivedEmail] = (receivedEmails as Messages).items;
|
||||
if (subject) {
|
||||
expect(firstReceivedEmail.subject).toBe(subject);
|
||||
}
|
||||
if (!returnLink) return;
|
||||
const dom = new JSDOM(firstReceivedEmail.html);
|
||||
const anchor = dom.window.document.querySelector(`a[href*="${returnLink}"]`);
|
||||
return anchor?.getAttribute("href");
|
||||
}
|
||||
|
||||
export async function expectExistingUserToBeInvitedToOrganization(
|
||||
page: Page,
|
||||
emails: ReturnType<typeof createEmailsFixture>,
|
||||
userEmail: string,
|
||||
subject?: string | null
|
||||
) {
|
||||
return expectInvitationEmailToBeReceived(page, emails, userEmail, subject, "teams?token");
|
||||
}
|
||||
Reference in New Issue
Block a user