-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: Add more orgs tests #12241
test: Add more orgs tests #12241
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,7 +37,7 @@ type SignupProps = inferSSRProps<typeof getServerSideProps>; | |
const checkValidEmail = (email: string) => z.string().email().safeParse(email).success; | ||
|
||
const getOrgUsernameFromEmail = (email: string, autoAcceptEmailDomain: string) => { | ||
const [emailUser, emailDomain] = email.split("@"); | ||
const [emailUser, emailDomain = ""] = email.split("@"); | ||
const username = | ||
emailDomain === autoAcceptEmailDomain | ||
? slugify(emailUser) | ||
|
@@ -143,7 +143,7 @@ export default function Signup({ prepopulateFormValues, token, orgSlug, orgAutoA | |
methods.clearErrors("apiError"); | ||
} | ||
|
||
if (methods.getValues().username === undefined && isOrgInviteByLink && orgAutoAcceptEmail) { | ||
if (!methods.getValues().username && isOrgInviteByLink && orgAutoAcceptEmail) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. empty username isn't valid so, let's compute username from email in that case as well. |
||
methods.setValue( | ||
"username", | ||
getOrgUsernameFromEmail(methods.getValues().email, orgAutoAcceptEmail) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type { Page } from "@playwright/test"; | ||
|
||
declare global { | ||
interface Window { | ||
E2E_CLIPBOARD_VALUE?: string; | ||
} | ||
} | ||
|
||
export type Window = typeof window; | ||
// creates the single server fixture | ||
export const createClipboardFixture = (page: Page) => { | ||
return { | ||
reset: async () => { | ||
await page.evaluate(() => { | ||
delete window.E2E_CLIPBOARD_VALUE; | ||
}); | ||
}, | ||
get: async () => { | ||
return getClipboardValue({ page }); | ||
}, | ||
}; | ||
}; | ||
|
||
function getClipboardValue({ page }: { page: Page }) { | ||
return page.evaluate(() => { | ||
return new Promise<string>((resolve, reject) => { | ||
setInterval(() => { | ||
if (!window.E2E_CLIPBOARD_VALUE) return; | ||
resolve(window.E2E_CLIPBOARD_VALUE); | ||
}, 500); | ||
setTimeout(() => reject(new Error("Timeout")), 1000); | ||
}); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { Page } from "@playwright/test"; | ||
import { expect } from "@playwright/test"; | ||
import { JSDOM } from "jsdom"; | ||
// eslint-disable-next-line no-restricted-imports | ||
import type { API, Messages } from "mailhog"; | ||
|
||
import { getEmailsReceivedByUser } from "../lib/testUtils"; | ||
|
||
export async function expectInvitationEmailToBeReceived( | ||
page: Page, | ||
emails: API | undefined, | ||
userEmail: string, | ||
subject: string, | ||
returnLink?: string | ||
) { | ||
if (!emails) return null; | ||
// We need to wait for the email to go through, otherwise it will fail | ||
// eslint-disable-next-line playwright/no-wait-for-timeout | ||
await page.waitForTimeout(5000); | ||
const receivedEmails = await getEmailsReceivedByUser({ emails, userEmail }); | ||
expect(receivedEmails?.total).toBe(1); | ||
Check failure on line 21 in apps/web/playwright/organization/expects.ts GitHub Actions / E2E tests / E2E tests (4/5)[@calcom/web] › apps/web/playwright/organization/organization-creation.e2e.ts:21:7 › Organization › should be able to create an organization and complete onboarding
Check failure on line 21 in apps/web/playwright/organization/expects.ts GitHub Actions / E2E tests / E2E tests (4/5)[@calcom/web] › apps/web/playwright/organization/organization-creation.e2e.ts:21:7 › Organization › should be able to create an organization and complete onboarding
|
||
const [firstReceivedEmail] = (receivedEmails as Messages).items; | ||
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"); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import { expect } from "@playwright/test"; | ||
import path from "path"; | ||
|
||
import { test } from "../lib/fixtures"; | ||
import { generateTotpCode } from "../lib/testUtils"; | ||
import { expectInvitationEmailToBeReceived } from "./expects"; | ||
|
||
test.afterAll(({ users, emails }) => { | ||
users.deleteAll(); | ||
emails?.deleteAll(); | ||
}); | ||
|
||
function capitalize(text: string) { | ||
if (!text) { | ||
return text; | ||
} | ||
return text.charAt(0).toUpperCase() + text.slice(1); | ||
} | ||
|
||
test.describe("Organization", () => { | ||
test("should be able to create an organization and complete onboarding", async ({ | ||
page, | ||
users, | ||
emails, | ||
}) => { | ||
const orgOwner = await users.create(); | ||
const orgDomain = `${orgOwner.username}-org`; | ||
const orgName = capitalize(`${orgOwner.username}-org`); | ||
await orgOwner.apiLogin(); | ||
await page.goto("/settings/organizations/new"); | ||
await page.waitForLoadState("networkidle"); | ||
|
||
await test.step("Basic info", async () => { | ||
// Check required fields | ||
await page.locator("button[type=submit]").click(); | ||
await expect(page.locator(".text-red-700")).toHaveCount(3); | ||
|
||
// Happy path | ||
await page.locator("input[name=adminEmail]").fill(`john@${orgDomain}.com`); | ||
expect(await page.locator("input[name=name]").inputValue()).toEqual(orgName); | ||
expect(await page.locator("input[name=slug]").inputValue()).toEqual(orgDomain); | ||
await page.locator("button[type=submit]").click(); | ||
await page.waitForLoadState("networkidle"); | ||
|
||
// Check admin email about code verification | ||
await expectInvitationEmailToBeReceived( | ||
page, | ||
emails, | ||
`john@${orgOwner.username}-org.com`, | ||
"Verify your email to create an organization" | ||
); | ||
|
||
await test.step("Verification", async () => { | ||
// Code verification | ||
await expect(page.locator("#modal-title")).toBeVisible(); | ||
await page.locator("input[name='2fa1']").fill(generateTotpCode(`john@${orgDomain}.com`)); | ||
await page.locator("button:text('Verify')").click(); | ||
|
||
// Check admin email about DNS pending action | ||
await expectInvitationEmailToBeReceived( | ||
page, | ||
emails, | ||
"[email protected]", | ||
"New organization created: pending action" | ||
); | ||
|
||
// Waiting to be in next step URL | ||
await page.waitForURL("/settings/organizations/*/set-password"); | ||
}); | ||
}); | ||
|
||
await test.step("Admin password", async () => { | ||
// Check required fields | ||
await page.locator("button[type=submit]").click(); | ||
await expect(page.locator(".text-red-700")).toHaveCount(3); // 3 password hints | ||
|
||
// Happy path | ||
await page.locator("input[name='password']").fill("ADMIN_user2023$"); | ||
await page.locator("button[type=submit]").click(); | ||
|
||
// Waiting to be in next step URL | ||
await page.waitForURL("/settings/organizations/*/about"); | ||
}); | ||
|
||
await test.step("About the organization", async () => { | ||
// Choosing an avatar | ||
await page.locator('button:text("Upload")').click(); | ||
const fileChooserPromise = page.waitForEvent("filechooser"); | ||
await page.getByText("Choose a file...").click(); | ||
const fileChooser = await fileChooserPromise; | ||
await fileChooser.setFiles(path.join(__dirname, "../../public/apple-touch-icon.png")); | ||
await page.locator('button:text("Save")').click(); | ||
|
||
// About text | ||
await page.locator('textarea[name="about"]').fill("This is a testing org"); | ||
await page.locator("button[type=submit]").click(); | ||
|
||
// Waiting to be in next step URL | ||
await page.waitForURL("/settings/organizations/*/onboard-admins"); | ||
}); | ||
|
||
await test.step("On-board administrators", async () => { | ||
// Required field | ||
await page.locator("button[type=submit]").click(); | ||
|
||
// Happy path | ||
await page.locator('textarea[name="emails"]').fill(`rick@${orgDomain}.com`); | ||
await page.locator("button[type=submit]").click(); | ||
|
||
// Check if invited admin received the invitation email | ||
await expectInvitationEmailToBeReceived( | ||
page, | ||
emails, | ||
`rick@${orgDomain}.com`, | ||
`${orgName}'s admin invited you to join the organization ${orgName} on Cal.com` | ||
); | ||
|
||
// Waiting to be in next step URL | ||
await page.waitForURL("/settings/organizations/*/add-teams"); | ||
}); | ||
|
||
await test.step("Create teams", async () => { | ||
// Initial state | ||
await expect(page.locator('input[name="teams.0.name"]')).toHaveCount(1); | ||
await expect(page.locator('button:text("Continue")')).toBeDisabled(); | ||
|
||
// Filling one team | ||
await page.locator('input[name="teams.0.name"]').fill("Marketing"); | ||
await expect(page.locator('button:text("Continue")')).toBeEnabled(); | ||
|
||
// Adding another team | ||
await page.locator('button:text("Add a team")').click(); | ||
await expect(page.locator('button:text("Continue")')).toBeDisabled(); | ||
await expect(page.locator('input[name="teams.1.name"]')).toHaveCount(1); | ||
await page.locator('input[name="teams.1.name"]').fill("Sales"); | ||
await expect(page.locator('button:text("Continue")')).toBeEnabled(); | ||
|
||
// Finishing the creation wizard | ||
await page.locator('button:text("Continue")').click(); | ||
await page.waitForURL("/event-types"); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue arises when form is submitted without entering the email or email doesn't have @ in it when it was submitted.