mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
* refactor: apply biome formatting to apps/web (batch 1) Formats apps/web non-modules directories: - apps/web/app - apps/web/lib - apps/web/pages - apps/web/styles - apps/web/server - apps/web/test - Root-level .ts and .mjs files Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to apps/web/modules (batch 2) Formats smaller apps/web/modules directories: - onboarding, data-table, users, apps, auth - integration-attribute-sync, shell, availability - feature-flags, team, webhooks, getting-started - feature-opt-in, troubleshooter, schedules, notifications - signup-view.tsx Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to apps/web/modules (batch 3) Formats medium apps/web/modules directories: - bookings - event-types - insights - embed Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to apps/web/modules/ee (batch 4) Formats apps/web/modules/ee directory. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to apps/web (batch 5) Formats remaining apps/web directories: - apps/web/modules/settings - apps/web/modules/booking-audit - apps/web/playwright Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to apps/web/components (batch 6) Formats remaining apps/web/components files. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to new apps/web files from main Formats newly added/modified files from main merge: - WorkflowStepContainer.tsx (resolved merge conflicts) - Newly moved files (blocklist, form-builder, schedules, etc.) - Other files modified in main Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor: apply biome formatting to new apps/web files from main Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
175 lines
5.3 KiB
TypeScript
175 lines
5.3 KiB
TypeScript
import { expect } from "@playwright/test";
|
|
import { randomBytes } from "node:crypto";
|
|
|
|
import { OAUTH_ERROR_REASONS } from "@calcom/features/oauth/services/OAuthService";
|
|
import type { PrismaClient } from "@calcom/prisma";
|
|
|
|
import { test } from "../lib/fixtures";
|
|
|
|
test.describe("OAuth authorize - client approval status", () => {
|
|
test.afterEach(async ({ prisma, users }, testInfo) => {
|
|
const testPrefix = `e2e-oauth-authorize-status-${testInfo.testId}-`;
|
|
|
|
await prisma.oAuthClient.deleteMany({
|
|
where: {
|
|
name: {
|
|
startsWith: testPrefix,
|
|
},
|
|
},
|
|
});
|
|
|
|
await users.deleteAll();
|
|
});
|
|
|
|
async function createOAuthClient({
|
|
prisma,
|
|
name,
|
|
status,
|
|
}: {
|
|
prisma: PrismaClient;
|
|
name: string;
|
|
status: "PENDING" | "APPROVED" | "REJECTED";
|
|
}) {
|
|
const clientId = randomBytes(32).toString("hex");
|
|
|
|
const client = await prisma.oAuthClient.create({
|
|
data: {
|
|
clientId,
|
|
name,
|
|
redirectUri: "https://example.com",
|
|
clientSecret: null,
|
|
clientType: "CONFIDENTIAL",
|
|
status,
|
|
},
|
|
});
|
|
|
|
return client;
|
|
}
|
|
|
|
test("PENDING client renders error on authorize page (no redirect)", async ({
|
|
page,
|
|
users,
|
|
prisma,
|
|
}, testInfo) => {
|
|
const user = await users.create({ username: "oauth-authorize-pending" });
|
|
await user.apiLogin();
|
|
|
|
const testPrefix = `e2e-oauth-authorize-status-${testInfo.testId}-`;
|
|
const client = await createOAuthClient({
|
|
prisma,
|
|
name: `${testPrefix}pending-${Date.now()}`,
|
|
status: "PENDING",
|
|
});
|
|
|
|
await page.goto(
|
|
`auth/oauth2/authorize?client_id=${client.clientId}&redirect_uri=${client.redirectUri}&state=1234`
|
|
);
|
|
|
|
// Should NOT redirect, should show error on page
|
|
await expect(page).not.toHaveURL(/^https:\/\/example\.com/);
|
|
|
|
// Check error message is displayed
|
|
await expect(page.getByText(OAUTH_ERROR_REASONS["client_not_approved"])).toBeVisible();
|
|
});
|
|
|
|
test("REJECTED client renders error on authorize page (no redirect)", async ({
|
|
page,
|
|
users,
|
|
prisma,
|
|
}, testInfo) => {
|
|
const user = await users.create({ username: "oauth-authorize-rejected" });
|
|
await user.apiLogin();
|
|
|
|
const testPrefix = `e2e-oauth-authorize-status-${testInfo.testId}-`;
|
|
const client = await createOAuthClient({
|
|
prisma,
|
|
name: `${testPrefix}rejected-${Date.now()}`,
|
|
status: "REJECTED",
|
|
});
|
|
|
|
await page.goto(
|
|
`auth/oauth2/authorize?client_id=${client.clientId}&redirect_uri=${client.redirectUri}&state=1234`
|
|
);
|
|
|
|
// Should NOT redirect, should show error on page
|
|
await expect(page).not.toHaveURL(/^https:\/\/example\.com/);
|
|
|
|
// Check error message is displayed
|
|
await expect(page.getByText(OAUTH_ERROR_REASONS["client_not_approved"])).toBeVisible();
|
|
});
|
|
|
|
test("APPROVED client redirects to redirectUri with code", async ({ page, users, prisma }, testInfo) => {
|
|
const user = await users.create({ username: "oauth-authorize-approved" });
|
|
await user.apiLogin();
|
|
|
|
const testPrefix = `e2e-oauth-authorize-status-${testInfo.testId}-`;
|
|
const client = await createOAuthClient({
|
|
prisma,
|
|
name: `${testPrefix}approved-${Date.now()}`,
|
|
status: "APPROVED",
|
|
});
|
|
|
|
await page.goto(
|
|
`auth/oauth2/authorize?client_id=${client.clientId}&redirect_uri=${client.redirectUri}&state=1234`
|
|
);
|
|
|
|
await page.waitForSelector('[data-testid="allow-button"]');
|
|
await page.getByTestId("allow-button").click();
|
|
|
|
await page.waitForFunction(() => {
|
|
return window.location.href.startsWith("https://example.com");
|
|
});
|
|
|
|
await expect(page).toHaveURL(/^https:\/\/example\.com/);
|
|
|
|
const url = new URL(page.url());
|
|
expect(url.searchParams.get("code")).toBeTruthy();
|
|
expect(url.searchParams.get("state")).toBe("1234");
|
|
expect(url.searchParams.get("error")).toBeNull();
|
|
});
|
|
|
|
test("redirect_uri mismatch renders error on authorize page (no redirect)", async ({
|
|
page,
|
|
users,
|
|
prisma,
|
|
}, testInfo) => {
|
|
const user = await users.create({ username: "oauth-authorize-redirect-uri-mismatch" });
|
|
await user.apiLogin();
|
|
|
|
const testPrefix = `e2e-oauth-authorize-status-${testInfo.testId}-`;
|
|
const client = await createOAuthClient({
|
|
prisma,
|
|
name: `${testPrefix}mismatch-${Date.now()}`,
|
|
status: "APPROVED",
|
|
});
|
|
|
|
const mismatchedRedirectUri = "https://example.com/mismatch";
|
|
|
|
await page.goto(
|
|
`auth/oauth2/authorize?client_id=${client.clientId}&redirect_uri=${encodeURIComponent(
|
|
mismatchedRedirectUri
|
|
)}&state=1234`
|
|
);
|
|
|
|
await expect(page).toHaveURL(/\/auth\/oauth2\/authorize/);
|
|
await expect(page.getByText(OAUTH_ERROR_REASONS["redirect_uri_mismatch"])).toBeVisible();
|
|
});
|
|
|
|
test("invalid client_id renders error on authorize page (no redirect)", async ({ page, users }) => {
|
|
const user = await users.create({ username: "oauth-authorize-invalid-client" });
|
|
await user.apiLogin();
|
|
|
|
const invalidClientId = "invalid-client-id-" + Date.now();
|
|
const redirectUri = "https://example.com";
|
|
|
|
await page.goto(
|
|
`auth/oauth2/authorize?client_id=${invalidClientId}&redirect_uri=${encodeURIComponent(
|
|
redirectUri
|
|
)}&state=1234`
|
|
);
|
|
|
|
await expect(page).toHaveURL(/\/auth\/oauth2\/authorize/);
|
|
await expect(page.getByText(OAUTH_ERROR_REASONS["client_not_found"])).toBeVisible();
|
|
});
|
|
});
|