diff --git a/.github/workflows/e2e-app-store.yml b/.github/workflows/e2e-app-store.yml index deb42ffe07..75dca65ec1 100644 --- a/.github/workflows/e2e-app-store.yml +++ b/.github/workflows/e2e-app-store.yml @@ -1,7 +1,7 @@ name: E2E App-Store Apps on: push: - branches: [ feature/event-routing ] + branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main pull_request_target: # So we can test on forks branches: - main @@ -86,14 +86,14 @@ jobs: restore-keys: cache-playwright- - run: yarn --frozen-lockfile - name: Install playwright deps - # if: steps.playwright-cache.outputs.cache-hit != 'true' + if: steps.playwright-cache.outputs.cache-hit != 'true' run: yarn playwright install --with-deps - name: Run Tests - run: yarn app-e2e-quick + run: yarn test-e2e:app-store - name: Upload Test Results if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: test-results-core - path: packages/app-store/**/playwright/results \ No newline at end of file + path: packages/app-store/**/playwright/results diff --git a/.github/workflows/e2e-embed.yml b/.github/workflows/e2e-embed.yml index 719a1e2b94..0617591190 100644 --- a/.github/workflows/e2e-embed.yml +++ b/.github/workflows/e2e-embed.yml @@ -79,7 +79,7 @@ jobs: restore-keys: cache-playwright- - run: yarn --frozen-lockfile - name: Install playwright deps - # if: steps.playwright-cache.outputs.cache-hit != 'true' + if: steps.playwright-cache.outputs.cache-hit != 'true' run: yarn playwright install --with-deps - name: Run Tests run: yarn turbo run embed-tests-update-snapshots:ci --scope=@calcom/embed-react --concurrency=1 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 0b702c2a6f..48a2b1fa8a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -1,5 +1,7 @@ name: E2E test on: + push: + branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main pull_request_target: # So we can test on forks branches: - main diff --git a/.github/workflows/integrations-third-party.yml b/.github/workflows/integrations-third-party.yml deleted file mode 100644 index 2a7876deda..0000000000 --- a/.github/workflows/integrations-third-party.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: E2E Test - Integrations with Third Party -on: - push: - branches: [ tests/with-msw ] - pull_request_target: # So we can test on forks - branches: - - main - paths-ignore: - - apps/api/** - - apps/console/** - - apps/docs/** - - apps/swagger/** - - apps/website/** - - apps/web/public/** -jobs: - test: - timeout-minutes: 20 - name: E2E Integration - strategy: - matrix: - node: ["16.x"] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - - env: - DATABASE_URL: postgresql://postgres:@localhost:5432/calendso - NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000 - NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000 - NEXTAUTH_SECRET: secret - GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }} - GOOGLE_LOGIN_ENABLED: true - # CRON_API_KEY: xxx - CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }} - NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }} - STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }} - STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }} - STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }} - PAYMENT_FEE_PERCENTAGE: 0.005 - PAYMENT_FEE_FIXED: 10 - SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso - SAML_ADMINS: pro@example.com - NEXTAUTH_URL: http://localhost:3000/api/auth - ZOOM_CLIENT_ID: ZOOM_CLIENT_ID - ZOOM_CLIENT_SECRET: ZOOM_CLIENT_SECRET - HUBSPOT_CLIENT_ID: HUBSPOT_CLIENT_ID - HUBSPOT_CLIENT_SECRET: HUBSPOT_CLIENT_SECRET - NEXT_PUBLIC_IS_E2E: 1 - # EMAIL_FROM: e2e@cal.com - # EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }} - # EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }} - # EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }} - # EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }} - # MS_GRAPH_CLIENT_ID: xxx - # MS_GRAPH_CLIENT_SECRET: xxx - # ZOOM_CLIENT_ID: xxx - # ZOOM_CLIENT_SECRET: xxx - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - services: - postgres: - image: postgres:12.1 - env: - POSTGRES_USER: postgres - POSTGRES_DB: calendso - ports: - - 5432:5432 - - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} # So we can test on forks - fetch-depth: 2 - - run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - cache: "yarn" - - - name: Cache playwright binaries - uses: actions/cache@v2 - id: playwright-cache - with: - path: | - ~/Library/Caches/ms-playwright - ~/.cache/ms-playwright - ${{ github.workspace }}/node_modules/playwright - key: cache-playwright-${{ hashFiles('**/yarn.lock') }} - restore-keys: cache-playwright- - - run: yarn --frozen-lockfile - - name: Install playwright deps - # if: steps.playwright-cache.outputs.cache-hit != 'true' - run: yarn playwright install --with-deps - - name: Run Tests - # Force bypass cache because new environment variables were added that caused DB to change but build remains cached - run: yarn test-e2e-integrations --force - - - name: Upload Test Results - if: ${{ always() }} - uses: actions/upload-artifact@v2 - with: - name: test-results-core - path: apps/web/playwright-integrations/test-results \ No newline at end of file diff --git a/.github/workflows/required-checks.yml b/.github/workflows/required-checks.yml index 4a50b02bc0..69b853e513 100644 --- a/.github/workflows/required-checks.yml +++ b/.github/workflows/required-checks.yml @@ -1,5 +1,7 @@ name: "Meta Workflow: Require Conditional Status Checks" on: + push: + branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main pull_request: branches: - main @@ -34,3 +36,7 @@ jobs: paths: - /apps/web/** - /packages/embeds/** + - job: test + paths: + - /apps/web/** + - /packages/** diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..550ed58c5c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Unit tests +on: + push: + branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main + pull_request_target: # So we can test on forks + branches: + - main + paths: + - apps/web/** + - packages/** +jobs: + test: + timeout-minutes: 20 + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} # So we can test on forks + fetch-depth: 2 + - run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV + - name: Use Node 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: "yarn" + - run: yarn --frozen-lockfile + - run: yarn test diff --git a/README.md b/README.md index 4309a95cee..26d7519cf4 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ Be sure to set the environment variable `NEXTAUTH_URL` to the correct value. If yarn test-e2e # To open last HTML report run: -yarn workspace @calcom/web playwright-report +yarn playwright show-report test-results/reports/playwright-html-report ``` ### Upgrading from earlier versions diff --git a/apps/web/package.json b/apps/web/package.json index b31d282c43..d77134c99c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,11 +9,6 @@ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next", "dev": "next dev", "dx": "yarn dev", - "test-e2e": "NEXT_PUBLIC_IS_E2E=1 yarn playwright test --config=../../tests/config/playwright.config.ts --project=chromium", - "test-e2e-integrations": "NEXT_PUBLIC_IS_E2E=1 yarn playwright test --config=playwright-integrations/config/playwright.config.ts --project=chromium", - "test-e2e-integrations-quick": "QUICK=true E2E_DEV_SERVER=1 yarn test-e2e-integrations", - "db-setup-tests": "dotenv -e ./test/.env.test -- yarn workspace @calcom/prisma prisma generate", - "playwright-report": "playwright show-report playwright/reports/playwright-html-report", "test-codegen": "yarn playwright codegen http://localhost:3000", "type-check": "tsc --pretty --noEmit", "build": "next build", diff --git a/apps/web/playwright-integrations/.gitignore b/apps/web/playwright-integrations/.gitignore deleted file mode 100644 index 5b8b3b6fca..0000000000 --- a/apps/web/playwright-integrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test-results \ No newline at end of file diff --git a/apps/web/playwright-integrations/config/playwright.config.ts b/apps/web/playwright-integrations/config/playwright.config.ts deleted file mode 100644 index 7fe9d923d8..0000000000 --- a/apps/web/playwright-integrations/config/playwright.config.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { devices, PlaywrightTestConfig } from "@playwright/test"; -import dotenv from "dotenv"; -import { addAliases } from "module-alias"; -import * as path from "path"; - -dotenv.config({ path: "./env" }); - -// Add aliases for the paths specified in the tsconfig.json file. -// This is needed because playwright does not consider tsconfig.json -// For more info, see: -// https://stackoverflow.com/questions/69023682/typescript-playwright-error-cannot-find-module -// https://github.com/microsoft/playwright/issues/7066#issuecomment-983984496 -addAliases({ - "@components": __dirname + "/apps/web/components", - "@lib": __dirname + "/apps/web/lib", - "@server": __dirname + "/apps/web/server", - "@ee": __dirname + "/apps/web/ee", -}); - -const outputDir = path.join(__dirname, "..", "test-results"); -const testDir = path.join(__dirname, "..", "tests"); - -const DEFAULT_NAVIGATION_TIMEOUT = 600000; - -const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS; -process.env.PLAYWRIGHT_TEST_BASE_URL = "http://localhost:3000"; -const quickMode = process.env.QUICK === "true"; - -const config: PlaywrightTestConfig = { - forbidOnly: !!process.env.CI, - retries: 0, - workers: quickMode ? 1 : 1, - timeout: 60_000, - maxFailures: headless ? 10 : undefined, - reporter: [ - [process.env.CI ? "github" : "list"], - ["html", { outputFolder: path.join(outputDir, "reports/playwright-html-report"), open: "never" }], - ["junit", { outputFile: path.join(outputDir, "reports/results.xml") }], - ], - outputDir, - use: { - baseURL: "http://localhost:3000/", - locale: "en-US", - trace: "retain-on-failure", - headless, - }, - projects: [ - { - name: "chromium", - testDir, - use: { - ...devices["Desktop Chrome"], - /** If navigation takes more than this, then something's wrong, let's fail fast. */ - navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT, - }, - }, - /* { - name: "firefox", - use: { ...devices["Desktop Firefox"] }, - }, - { - name: "webkit", - use: { ...devices["Desktop Safari"] }, - }, */ - ], -}; - -export default config; diff --git a/apps/web/playwright-integrations/lib/fixtures.ts b/apps/web/playwright-integrations/lib/fixtures.ts deleted file mode 100644 index 1f36e15566..0000000000 --- a/apps/web/playwright-integrations/lib/fixtures.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { test as base } from "@playwright/test"; -import { Server } from "http"; -import { rest } from "msw"; -import type { SetupServerApi } from "msw/node"; - -import { nextServer } from "../../playwright-integrations/next-server"; -import { createBookingsFixture } from "../../playwright/fixtures/bookings"; -import { createPaymentsFixture } from "../../playwright/fixtures/payments"; -import { createUsersFixture } from "../../playwright/fixtures/users"; - -interface Fixtures { - users: ReturnType; - bookings: ReturnType; - payments: ReturnType; - server: Server; - requestInterceptor: SetupServerApi; - rest: typeof rest; -} - -/** - * @see https://playwright.dev/docs/test-fixtures - */ -export const test = base.extend({ - users: async ({ page }, use, workerInfo) => { - const usersFixture = createUsersFixture(page, workerInfo); - await use(usersFixture); - }, - bookings: async ({ page }, use) => { - const bookingsFixture = createBookingsFixture(page); - await use(bookingsFixture); - }, - payments: async ({ page }, use) => { - const payemntsFixture = createPaymentsFixture(page); - await use(payemntsFixture); - }, - // This fixture runs for each worker, ensuring that every worker starts it's own Next.js instance on which we can attach MSW - // A single worker can run many tests - server: [ - async ({}, use) => { - const server = await nextServer(); - await use(server); - server.close(); - }, - { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - scope: "worker", - auto: true, - }, - ], -}); diff --git a/apps/web/playwright/app-store.test.ts b/apps/web/playwright/app-store.e2e.ts similarity index 91% rename from apps/web/playwright/app-store.test.ts rename to apps/web/playwright/app-store.e2e.ts index 515d1088fd..464e9e9e6d 100644 --- a/apps/web/playwright/app-store.test.ts +++ b/apps/web/playwright/app-store.e2e.ts @@ -5,13 +5,15 @@ import { test } from "./lib/fixtures"; test.describe.configure({ mode: "parallel" }); test.describe("App Store - Authed", () => { - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - test("Browse apple-calendar and try to install", async ({ page }) => { + test("Browse apple-calendar and try to install", async ({ page, users }) => { + const pro = await users.create(); + await pro.login(); await page.goto("/apps"); await page.click('[data-testid="app-store-category-calendar"]'); await page.click('[data-testid="app-store-app-card-apple-calendar"]'); await page.click('[data-testid="install-app-button"]'); await expect(page.locator(`text=Connect to Apple Server`)).toBeVisible(); + await pro.delete(); }); }); diff --git a/apps/web/playwright/auth/auth-index.e2e.ts b/apps/web/playwright/auth/auth-index.e2e.ts new file mode 100644 index 0000000000..4de1422ace --- /dev/null +++ b/apps/web/playwright/auth/auth-index.e2e.ts @@ -0,0 +1,102 @@ +import { expect } from "@playwright/test"; + +import { WEBAPP_URL } from "@calcom/lib/constants"; + +import { test } from "../lib/fixtures"; +import { todo } from "../lib/testUtils"; + +test.describe("Can signup from a team invite", async () => { + test.beforeEach(async ({ users }) => { + const proUser = await users.create(); + await proUser.login(); + }); + test.afterEach(async ({ users }) => users.deleteAll()); + + test("Team invites validations work and can accept invite", async ({ browser, page, users, prisma }) => { + const [proUser] = users.get(); + const teamName = `${proUser.username}'s Team`; + const testUser = { + username: `${proUser.username}-member`, + password: `${proUser.username}-member`, + email: `${proUser.username}-member@example.com`, + }; + await page.goto("/settings/teams"); + + // Create a new team + await page.click("text=New Team"); + await page.fill('input[id="name"]', teamName); + await page.click('[data-testid="create-new-team-button"]'); + // Go to new team page + await page.click(`a[title="${teamName}"]`); + // Add new member to team + await page.click('[data-testid="new-member-button"]'); + await page.fill('input[id="inviteUser"]', testUser.email); + await page.click('[data-testid="invite-new-member-button"]'); + + // Wait for the invite to be sent + await page.waitForSelector(`[data-testid="member-email"][data-email="${testUser.email}"]`); + + const tokenObj = await prisma.verificationToken.findFirstOrThrow({ + where: { identifier: testUser.email }, + select: { token: true }, + }); + + if (!proUser.username) throw Error("Test username is null, can't continue"); + + // Open a new user window to accept the invite + const newPage = await browser.newPage(); + await newPage.goto(`/auth/signup?token=${tokenObj.token}&callbackUrl=${WEBAPP_URL}/settings/teams`); + + // Fill in form + await newPage.fill('input[name="username"]', proUser.username); // Invalid username + await newPage.fill('input[name="email"]', testUser.email); + await newPage.fill('input[name="password"]', testUser.password); + await newPage.fill('input[name="passwordcheck"]', testUser.password); + await newPage.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit + + await expect(newPage.locator('text="Username already taken"')).toBeVisible(); + + // Email address is already registered + // TODO: Form errors don't disappear when corrected and resubmitted, so we need to refresh + await newPage.reload(); + await newPage.fill('input[name="username"]', testUser.username); + await newPage.fill('input[name="email"]', `${proUser.username}@example.com`); // Taken email + await newPage.fill('input[name="password"]', testUser.password); + await newPage.fill('input[name="passwordcheck"]', testUser.password); + await newPage.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit + await expect(newPage.locator('text="Email address is already registered"')).toBeVisible(); + + // Successful signup + // TODO: Form errors don't disappear when corrected and resubmitted, so we need to refresh + await newPage.reload(); + await newPage.fill('input[name="username"]', testUser.username); + await newPage.fill('input[name="email"]', testUser.email); + await newPage.fill('input[name="password"]', testUser.password); + await newPage.fill('input[name="passwordcheck"]', testUser.password); + await newPage.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit + await expect(newPage.locator(`[data-testid="login-form"]`)).toBeVisible(); + + // We don't need the new browser anymore + await newPage.close(); + + const createdUser = await prisma.user.findUniqueOrThrow({ + where: { email: testUser.email }, + include: { teams: { include: { team: true } } }, + }); + + console.log("createdUser", createdUser); + + // Check that the user was created + expect(createdUser).not.toBeNull(); + expect(createdUser.username).toBe(testUser.username); + expect(createdUser.password).not.toBeNull(); + expect(createdUser.emailVerified).not.toBeNull(); + // Check that the user accepted the team invite + expect(createdUser.teams).toHaveLength(1); + expect(createdUser.teams[0].team.name).toBe(teamName); + expect(createdUser.teams[0].role).toBe("MEMBER"); + expect(createdUser.teams[0].accepted).toBe(true); + }); +}); + +todo("Can login using 2FA"); diff --git a/apps/web/playwright/auth/auth-index.test.ts b/apps/web/playwright/auth/auth-index.test.ts deleted file mode 100644 index 4a9260bfe0..0000000000 --- a/apps/web/playwright/auth/auth-index.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { expect, test } from "@playwright/test"; - -import prisma from "@calcom/prisma"; - -import { WEBAPP_URL } from "@lib/config/constants"; - -import { todo } from "../lib/testUtils"; - -test.describe("Can signup from a team invite", async () => { - let page; - let token: string | undefined; - let signupFromInviteURL = ""; - const team = { name: "Seeded Team", slug: "seeded-team" }; - const testUser = { - email: "test@test.com", - password: "secretpassword123", - validUsername: "test-user", - }; - const usernameAlreadyTaken = "teampro"; - const emailAlreadyTaken = "teampro@example.com"; - - test.use({ storageState: "playwright/artifacts/teamproStorageState.json" }); - test.beforeAll(async ({ browser }) => { - page = await browser.newPage(); - - await page.goto("/settings/teams"); - - await page.waitForSelector(`a[title="${team.name}"]`); - await page.click(`a[title="${team.name}"]`); - - // Send invite to team - await page.click('[data-testid="new-member-button"]'); - await page.fill('input[id="inviteUser"]', testUser.email); - await page.click('[data-testid="invite-new-member-button"]'); - - // Wait for the invite to be sent - await page.waitForSelector(`[data-testid="member-email"][data-email="${testUser.email}"]`); - - const tokenObj = await prisma.verificationToken.findFirst({ - where: { identifier: testUser.email }, - select: { token: true }, - }); - token = tokenObj?.token; - signupFromInviteURL = `/auth/signup?token=${token}&callbackUrl=${WEBAPP_URL}/settings/teams`; - }); - - test.afterAll(async () => { - // Delete test user - await prisma.user.delete({ - where: { email: testUser.email }, - }); - // Delete verification request - await prisma.verificationToken.delete({ - where: { token }, - }); - }); - - test("Username already taken", async ({ page }) => { - expect(token).toBeDefined(); - await page.goto(signupFromInviteURL); - // Fill in form - await page.fill('input[name="username"]', usernameAlreadyTaken); - await page.fill('input[name="email"]', testUser.email); - await page.fill('input[name="password"]', testUser.password); - await page.fill('input[name="passwordcheck"]', testUser.password); - await page.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit - - await expect(page.locator('text="Username already taken"')).toBeVisible(); - }); - - test("Email address is already registered", async ({ page }) => { - expect(token).toBeDefined(); - await page.goto(signupFromInviteURL); - // Fill in form - await page.fill('input[name="username"]', testUser.validUsername); - await page.fill('input[name="email"]', emailAlreadyTaken); - await page.fill('input[name="password"]', testUser.password); - await page.fill('input[name="passwordcheck"]', testUser.password); - await page.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit - - await expect(page.locator('text="Email address is already registered"')).toBeVisible(); - }); - - test("Successful signup", async ({ page }) => { - expect(token).toBeDefined(); - await page.goto(signupFromInviteURL); - // Fill in form - await page.fill('input[name="username"]', testUser.validUsername); - await page.fill('input[name="email"]', testUser.email); - await page.fill('input[name="password"]', testUser.password); - await page.fill('input[name="passwordcheck"]', testUser.password); - await page.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit - - await page.waitForNavigation(); - - const createdUser = await prisma.user.findUnique({ - where: { email: testUser.email }, - include: { - teams: { - include: { - team: true, - }, - }, - }, - }); - // Check that the user was created - expect(createdUser).not.toBeNull(); - expect(createdUser?.username).toBe(testUser.validUsername); - expect(createdUser?.password).not.toBeNull(); - expect(createdUser?.emailVerified).not.toBeNull(); - // Check that the user accepted the team invite - expect(createdUser?.teams).toHaveLength(1); - expect(createdUser?.teams[0].team.name).toBe(team.name); - expect(createdUser?.teams[0].team.slug).toBe(team.slug); - expect(createdUser?.teams[0].role).toBe("MEMBER"); - expect(createdUser?.teams[0].accepted).toBe(true); - }); -}); - -todo("Can login using 2FA"); diff --git a/apps/web/playwright/auth/delete-account.test.ts b/apps/web/playwright/auth/delete-account.e2e.ts similarity index 100% rename from apps/web/playwright/auth/delete-account.test.ts rename to apps/web/playwright/auth/delete-account.e2e.ts diff --git a/apps/web/playwright/auth/forgot-password.test.ts b/apps/web/playwright/auth/forgot-password.e2e.ts similarity index 100% rename from apps/web/playwright/auth/forgot-password.test.ts rename to apps/web/playwright/auth/forgot-password.e2e.ts diff --git a/apps/web/playwright/booking-pages.test.ts b/apps/web/playwright/booking-pages.e2e.ts similarity index 100% rename from apps/web/playwright/booking-pages.test.ts rename to apps/web/playwright/booking-pages.e2e.ts diff --git a/apps/web/playwright/change-password.e2e.ts b/apps/web/playwright/change-password.e2e.ts new file mode 100644 index 0000000000..c76774ca60 --- /dev/null +++ b/apps/web/playwright/change-password.e2e.ts @@ -0,0 +1,23 @@ +import { expect } from "@playwright/test"; + +import { test } from "./lib/fixtures"; + +test.afterEach(({ users }) => users.deleteAll()); + +test.describe("Change Passsword Test", () => { + test("change password", async ({ page, users }) => { + const pro = await users.create(); + await pro.login(); + // Go to http://localhost:3000/settings/security + await page.goto("/settings/security"); + + if (!pro.username) throw Error("Test user doesn't have a username"); + + // Fill form + await page.fill('[name="current_password"]', pro.username); + await page.fill('[name="new_password"]', `${pro.username}1111`); + await page.press('[name="new_password"]', "Enter"); + + await expect(page.locator(`text=Your password has been successfully changed.`)).toBeVisible(); + }); +}); diff --git a/apps/web/playwright/change-password.test.ts b/apps/web/playwright/change-password.test.ts deleted file mode 100644 index f69ff704c1..0000000000 --- a/apps/web/playwright/change-password.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { expect, test } from "@playwright/test"; - -test.describe("Change Passsword Test", () => { - // Using logged in state from globalSteup - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - - test("change password", async ({ page }) => { - // Try to go homepage - await page.goto("/"); - // It should redirect you to the event-types page - await page.waitForSelector("[data-testid=event-types]"); - - // Go to http://localhost:3000/settings/security - await page.goto("/settings/security"); - - // Fill form - await page.fill('[name="current_password"]', "pro"); - await page.fill('[name="new_password"]', "pro1"); - await page.press('[name="new_password"]', "Enter"); - - await expect(page.locator(`text=Your password has been successfully changed.`)).toBeVisible(); - - // Let's revert back to prevent errors on other tests - await page.fill('[name="current_password"]', "pro1"); - await page.fill('[name="new_password"]', "pro"); - await page.press('[name="new_password"]', "Enter"); - - await expect(page.locator(`text=Your password has been successfully changed.`)).toBeVisible(); - }); -}); diff --git a/apps/web/playwright/change-username.test.ts b/apps/web/playwright/change-username.e2e.ts similarity index 95% rename from apps/web/playwright/change-username.test.ts rename to apps/web/playwright/change-username.e2e.ts index 0426e8c570..f16df172e8 100644 --- a/apps/web/playwright/change-username.test.ts +++ b/apps/web/playwright/change-username.e2e.ts @@ -5,7 +5,6 @@ import { getFreePlanPrice, getProPlanPrice } from "@calcom/app-store/stripepayme import dayjs from "@calcom/dayjs"; import stripe from "@calcom/features/ee/payments/server/stripe"; import { WEBAPP_URL } from "@calcom/lib/constants"; -import prisma from "@calcom/prisma"; import { test } from "./lib/fixtures"; @@ -26,7 +25,7 @@ test.describe("Change username on settings", () => { await users.deleteAll(); }); - test("User can change username", async ({ page, users }) => { + test("User can change username", async ({ page, users, prisma }) => { const user = await users.create({ plan: UserPlan.TRIAL }); await user.login(); @@ -45,12 +44,12 @@ test.describe("Change username on settings", () => { page.click("[data-testid=save-username]"), ]); - const newUpdatedUser = await prisma.user.findFirst({ + const newUpdatedUser = await prisma.user.findUniqueOrThrow({ where: { id: user.id, }, }); - expect(newUpdatedUser?.username).toBe("demousernamex"); + expect(newUpdatedUser.username).toBe("demousernamex"); }); test("User trial can update to PREMIUM username", async ({ page, users }, testInfo) => { diff --git a/apps/web/playwright/dynamic-booking-pages.test.ts b/apps/web/playwright/dynamic-booking-pages.e2e.ts similarity index 95% rename from apps/web/playwright/dynamic-booking-pages.test.ts rename to apps/web/playwright/dynamic-booking-pages.e2e.ts index 8dc7861582..279fd453aa 100644 --- a/apps/web/playwright/dynamic-booking-pages.test.ts +++ b/apps/web/playwright/dynamic-booking-pages.e2e.ts @@ -11,7 +11,6 @@ import { test.describe.configure({ mode: "parallel" }); test.describe("dynamic booking", () => { - test.skip(true, "TODO: Re-enable after v1.7 launch"); test.beforeEach(async ({ page, users }) => { const pro = await users.create(); await pro.login(); @@ -19,7 +18,7 @@ test.describe("dynamic booking", () => { await page.goto(`/${pro.username}+${free.username}`); }); - test.afterEach(async ({ page, users }) => { + test.afterEach(async ({ users }) => { await users.deleteAll(); }); diff --git a/apps/web/playwright/embed-code-generator.test.ts b/apps/web/playwright/embed-code-generator.e2e.ts similarity index 87% rename from apps/web/playwright/embed-code-generator.test.ts rename to apps/web/playwright/embed-code-generator.e2e.ts index 059cbcd7df..2b558872a7 100644 --- a/apps/web/playwright/embed-code-generator.test.ts +++ b/apps/web/playwright/embed-code-generator.e2e.ts @@ -1,4 +1,6 @@ -import { expect, Page, test } from "@playwright/test"; +import { expect, Page } from "@playwright/test"; + +import { test } from "./lib/fixtures"; function chooseEmbedType(page: Page, embedType: string) { page.locator(`[data-testid=${embedType}]`).click(); @@ -86,14 +88,18 @@ async function expectToContainValidPreviewIframe( test.describe.configure({ mode: "parallel" }); test.describe("Embed Code Generator Tests", () => { - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); + test.beforeEach(async ({ users }) => { + const pro = await users.create(); + await pro.login(); + }); test.describe("Event Types Page", () => { test.beforeEach(async ({ page }) => { await page.goto("/event-types"); }); - test("open Embed Dialog and choose Inline for First Event Type", async ({ page }) => { + test("open Embed Dialog and choose Inline for First Event Type", async ({ page, users }) => { + const [pro] = users.get(); const embedUrl = await clickFirstEventTypeEmbedButton(page); await expectToBeNavigatingToEmbedTypesDialog(page, { embedUrl, @@ -112,10 +118,15 @@ test.describe("Embed Code Generator Tests", () => { await gotToPreviewTab(page); - await expectToContainValidPreviewIframe(page, { embedType: "inline", calLink: "pro/30min" }); + await expectToContainValidPreviewIframe(page, { + embedType: "inline", + calLink: `${pro.username}/30-min`, + }); }); - test("open Embed Dialog and choose floating-popup for First Event Type", async ({ page }) => { + test("open Embed Dialog and choose floating-popup for First Event Type", async ({ page, users }) => { + const [pro] = users.get(); + const embedUrl = await clickFirstEventTypeEmbedButton(page); await expectToBeNavigatingToEmbedTypesDialog(page, { @@ -133,10 +144,14 @@ test.describe("Embed Code Generator Tests", () => { await expectToContainValidCode(page, { embedType: "floating-popup" }); await gotToPreviewTab(page); - await expectToContainValidPreviewIframe(page, { embedType: "floating-popup", calLink: "pro/30min" }); + await expectToContainValidPreviewIframe(page, { + embedType: "floating-popup", + calLink: `${pro.username}/30-min`, + }); }); - test("open Embed Dialog and choose element-click for First Event Type", async ({ page }) => { + test("open Embed Dialog and choose element-click for First Event Type", async ({ page, users }) => { + const [pro] = users.get(); const embedUrl = await clickFirstEventTypeEmbedButton(page); await expectToBeNavigatingToEmbedTypesDialog(page, { @@ -154,7 +169,10 @@ test.describe("Embed Code Generator Tests", () => { await expectToContainValidCode(page, { embedType: "element-click" }); await gotToPreviewTab(page); - await expectToContainValidPreviewIframe(page, { embedType: "element-click", calLink: "pro/30min" }); + await expectToContainValidPreviewIframe(page, { + embedType: "element-click", + calLink: `${pro.username}/30-min`, + }); }); }); diff --git a/apps/web/playwright/event-types.test.ts b/apps/web/playwright/event-types.e2e.ts similarity index 80% rename from apps/web/playwright/event-types.test.ts rename to apps/web/playwright/event-types.e2e.ts index db24426031..51cb70ad12 100644 --- a/apps/web/playwright/event-types.test.ts +++ b/apps/web/playwright/event-types.e2e.ts @@ -1,25 +1,23 @@ -import { expect, Locator, test } from "@playwright/test"; +import { expect } from "@playwright/test"; import { randomString } from "../lib/random"; -import { deleteEventTypeByTitle } from "./lib/teardown"; +import { test } from "./lib/fixtures"; test.describe.configure({ mode: "parallel" }); test.describe("Event Types tests", () => { - test.beforeEach(async ({ page }) => { - await page.goto("/event-types"); - // We wait until loading is finished - await page.waitForSelector('[data-testid="event-types"]'); - }); - test.describe("pro user", () => { - let isCreated: Locator; - let eventTitle: string; - - test.afterAll(async () => { - if (isCreated) await deleteEventTypeByTitle(eventTitle); + test.beforeEach(async ({ page, users }) => { + const proUser = await users.create(); + await proUser.login(); + await page.goto("/event-types"); + // We wait until loading is finished + await page.waitForSelector('[data-testid="event-types"]'); + }); + + test.afterEach(async ({ users }) => { + await users.deleteAll(); }); - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); test("has at least 2 events", async ({ page }) => { const $eventTypes = page.locator("[data-testid=event-types] > *"); @@ -34,7 +32,7 @@ test.describe("Event Types tests", () => { test("can add new event type", async ({ page }) => { await page.click("[data-testid=new-event-type]"); const nonce = randomString(3); - eventTitle = `hello ${nonce}`; + const eventTitle = `hello ${nonce}`; await page.fill("[name=title]", eventTitle); await page.fill("[name=length]", "10"); @@ -47,15 +45,13 @@ test.describe("Event Types tests", () => { }); await page.goto("/event-types"); - - isCreated = page.locator(`text='${eventTitle}'`); - await expect(isCreated).toBeVisible(); + await expect(page.locator(`text='${eventTitle}'`)).toBeVisible(); }); test("enabling recurring event comes with default options", async ({ page }) => { await page.click("[data-testid=new-event-type]"); const nonce = randomString(3); - eventTitle = `my recurring event ${nonce}`; + const eventTitle = `my recurring event ${nonce}`; await page.fill("[name=title]", eventTitle); await page.fill("[name=length]", "15"); @@ -70,8 +66,7 @@ test.describe("Event Types tests", () => { await page.click("[data-testid=show-advanced-settings]"); await expect(page.locator("[data-testid=recurring-event-collapsible]")).not.toBeVisible(); await page.click("[data-testid=recurring-event-check]"); - isCreated = page.locator("[data-testid=recurring-event-collapsible]"); - await expect(isCreated).toBeVisible(); + await expect(page.locator("[data-testid=recurring-event-collapsible]")).toBeVisible(); expect( await page @@ -91,8 +86,12 @@ test.describe("Event Types tests", () => { }); test("can duplicate an existing event type", async ({ page }) => { - // TODO: Locate the actual EventType available in list. This ID might change in future - const eventTypeId = "6"; + const firstElement = await page.waitForSelector( + '[data-testid="event-types"] a[href^="/event-types/"] >> nth=0' + ); + const href = await firstElement.getAttribute("href"); + if (!href) throw new Error("No href found for event type"); + const [eventTypeId] = href.split("/").reverse(); const firstTitle = await page.locator(`[data-testid=event-type-title-${eventTypeId}]`).innerText(); const firstFullSlug = await page.locator(`[data-testid=event-type-slug-${eventTypeId}]`).innerText(); const firstSlug = firstFullSlug.split("/")[2]; @@ -134,7 +133,13 @@ test.describe("Event Types tests", () => { }); test.describe("free user", () => { - test.use({ storageState: "playwright/artifacts/freeStorageState.json" }); + test.beforeEach(async ({ page, users }) => { + const free = await users.create({ plan: "FREE" }); + await free.login(); + await page.goto("/event-types"); + // We wait until loading is finished + await page.waitForSelector('[data-testid="event-types"]'); + }); test("has at least 2 events where first is enabled", async ({ page }) => { const $eventTypes = page.locator("[data-testid=event-types] > *"); diff --git a/apps/web/playwright/fixtures/embeds.ts b/apps/web/playwright/fixtures/embeds.ts new file mode 100644 index 0000000000..c96f5d5bc5 --- /dev/null +++ b/apps/web/playwright/fixtures/embeds.ts @@ -0,0 +1,81 @@ +import type { Page } from "@playwright/test"; + +export const createEmbedsFixture = (page: Page) => { + return async (calNamespace: string) => { + await page.addInitScript( + ({ calNamespace }: { calNamespace: string }) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + window.eventsFiredStoreForPlaywright = window.eventsFiredStoreForPlaywright || {}; + document.addEventListener("DOMContentLoaded", function tryAddingListener() { + if (parent !== window) { + // Firefox seems to execute this snippet for iframe as well. Avoid that. It must be executed only for parent frame. + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + window.initialBodyDisplay = document.body.style.display; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + window.initialBodyBackground = document.body.style.background; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + window.initialValuesSet = true; + + return; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + let api = window.Cal; + + if (!api) { + setTimeout(tryAddingListener, 500); + return; + } + if (calNamespace) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + api = window.Cal.ns[calNamespace]; + } + console.log("PlaywrightTest:", "Adding listener for __iframeReady"); + if (!api) { + throw new Error(`namespace "${calNamespace}" not found`); + } + api("on", { + action: "*", + callback: (e: any) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.iframeReady = true; // Technically if there are multiple cal embeds, it can be set due to some other iframe. But it works for now. Improve it when it doesn't work + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const store = window.eventsFiredStoreForPlaywright; + const eventStore = (store[`${e.detail.type}-${e.detail.namespace}`] = + store[`${e.detail.type}-${e.detail.namespace}`] || []); + eventStore.push(e.detail); + }, + }); + }); + }, + { calNamespace } + ); + }; +}; + +export const createGetActionFiredDetails = (page: Page) => { + return async ({ calNamespace, actionType }: { calNamespace: string; actionType: string }) => { + if (!page.isClosed()) { + return await page.evaluate( + ({ actionType, calNamespace }) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + return window.eventsFiredStoreForPlaywright[`${actionType}-${calNamespace}`]; + }, + { actionType, calNamespace } + ); + } + }; +}; diff --git a/apps/web/playwright/fixtures/servers.ts b/apps/web/playwright/fixtures/servers.ts new file mode 100644 index 0000000000..8f57542197 --- /dev/null +++ b/apps/web/playwright/fixtures/servers.ts @@ -0,0 +1,33 @@ +import type { Server } from "http"; + +import { nextServer } from "../lib/next-server"; + +type ServerFixture = ReturnType; + +// creates a servers fixture instance and stores the collection +export const createServersFixture = () => { + const store = { servers: [] } as { servers: ServerFixture[] }; + return { + create: async () => { + const server = await nextServer(); + const serverFixture = createServerFixture(server); + store.servers.push(serverFixture); + return serverFixture; + }, + get: () => store.servers, + deleteAll: async () => { + store.servers.forEach((server) => server.delete()); + store.servers = []; + }, + }; +}; + +// creates the single server fixture +const createServerFixture = (server: Server) => { + const store = { server }; + + return { + self: async () => store.server, + delete: async () => store.server.close(), + }; +}; diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index 06aa919b2f..386fb31cf0 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -71,7 +71,7 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => { }, get: () => store.users, logout: async () => { - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/auth/logout`); + await page.goto("/auth/logout"); }, deleteAll: async () => { const ids = store.users.map((u) => u.id); @@ -166,8 +166,7 @@ export async function login( const signInLocator = loginLocator.locator('[type="submit"]'); //login - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await page.goto(process.env.PLAYWRIGHT_TEST_BASE_URL!); + await page.goto("/"); await emailLocator.fill(user.email ?? `${user.username}@example.com`); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await passwordLocator.fill(user.password ?? user.username!); diff --git a/apps/web/playwright/hash-my-url.test.ts b/apps/web/playwright/hash-my-url.e2e.ts similarity index 100% rename from apps/web/playwright/hash-my-url.test.ts rename to apps/web/playwright/hash-my-url.e2e.ts diff --git a/apps/web/playwright/integrations-stripe.test.ts b/apps/web/playwright/integrations-stripe.e2e.ts similarity index 100% rename from apps/web/playwright/integrations-stripe.test.ts rename to apps/web/playwright/integrations-stripe.e2e.ts diff --git a/apps/web/playwright-integrations/tests/integrations.test.ts b/apps/web/playwright/integrations.e2e.ts similarity index 94% rename from apps/web/playwright-integrations/tests/integrations.test.ts rename to apps/web/playwright/integrations.e2e.ts index 389ca569f1..2acaa9c630 100644 --- a/apps/web/playwright-integrations/tests/integrations.test.ts +++ b/apps/web/playwright/integrations.e2e.ts @@ -4,14 +4,9 @@ import { setupServer } from "msw/node"; import { v4 as uuidv4 } from "uuid"; import { prisma } from "@calcom/prisma"; -import { - createHttpServer, - selectFirstAvailableTimeSlotNextMonth, - todo, - waitFor, -} from "@calcom/web/playwright/lib/testUtils"; -import { test } from "../lib/fixtures"; +import { test } from "./lib/fixtures"; +import { createHttpServer, selectFirstAvailableTimeSlotNextMonth, todo, waitFor } from "./lib/testUtils"; declare let global: { E2E_EMAILS?: ({ text: string } | Record)[]; @@ -23,11 +18,6 @@ const requestInterceptor = setupServer( return res(ctx.status(200)); }) ); -requestInterceptor.listen({ - // Comment this to log which all requests are going that are unmocked - onUnhandledRequest: "bypass", -}); -requestInterceptor.use(); const addOauthBasedIntegration = async function ({ page, @@ -76,7 +66,7 @@ const addOauthBasedIntegration = async function ({ }) ); - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/apps/${slug}`); + await page.goto(`/apps/${slug}`); await page.click('[data-testid="install-app-button"]'); }; @@ -113,7 +103,7 @@ async function bookEvent(page: Page, calLink: string) { // It would also allow correct snapshot to be taken for current month. // eslint-disable-next-line playwright/no-wait-for-timeout await page.waitForTimeout(1000); - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/${calLink}`); + await page.goto(`/${calLink}`); await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).click(); page.locator('[data-testid="time"]').nth(0).click(); @@ -155,7 +145,22 @@ async function bookEvent(page: Page, calLink: string) { test.describe.configure({ mode: "parallel" }); -test.describe("Integrations", () => { +// Enable API mocking before tests. +test.beforeAll(() => + requestInterceptor.listen({ + // Comment this to log which all requests are going that are unmocked + onUnhandledRequest: "bypass", + }) +); + +// Reset any runtime request handlers we may add during the tests. +test.afterEach(() => requestInterceptor.resetHandlers()); + +// Disable API mocking after the tests are done. +test.afterAll(() => requestInterceptor.close()); + +// TODO: Fix MSW mocking +test.fixme("Integrations", () => { test.beforeEach(() => { global.E2E_EMAILS = []; }); @@ -365,7 +370,7 @@ test.describe("Integrations", () => { const user = await users.create(); const [eventType] = user.eventTypes; await user.login(); - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/settings/developer`); + await page.goto(`/settings/developer`); // --- add webhook await page.click('[data-testid="new_webhook"]'); @@ -384,7 +389,7 @@ test.describe("Integrations", () => { expect(page.locator(`text='${webhookReceiver.url}'`)).toBeDefined(); // --- Book the first available day next month in the pro user's "30min"-event - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/${user.username}/${eventType.slug}`); + await page.goto(`/${user.username}/${eventType.slug}`); await selectFirstAvailableTimeSlotNextMonth(page); // --- fill form diff --git a/apps/web/playwright-integrations/tests/integrations.test.ts-snapshots/webhookResponse-chromium.txt b/apps/web/playwright/integrations.e2e.ts-snapshots/webhookResponse--calcom-web.txt similarity index 100% rename from apps/web/playwright-integrations/tests/integrations.test.ts-snapshots/webhookResponse-chromium.txt rename to apps/web/playwright/integrations.e2e.ts-snapshots/webhookResponse--calcom-web.txt diff --git a/apps/web/playwright/lib/fixtures.ts b/apps/web/playwright/lib/fixtures.ts index 0287e1c67a..e3c5cb6310 100644 --- a/apps/web/playwright/lib/fixtures.ts +++ b/apps/web/playwright/lib/fixtures.ts @@ -1,13 +1,21 @@ import { test as base } from "@playwright/test"; +import prisma from "@calcom/prisma"; + import { createBookingsFixture } from "../fixtures/bookings"; +import { createEmbedsFixture, createGetActionFiredDetails } from "../fixtures/embeds"; import { createPaymentsFixture } from "../fixtures/payments"; +import { createServersFixture } from "../fixtures/servers"; import { createUsersFixture } from "../fixtures/users"; interface Fixtures { users: ReturnType; bookings: ReturnType; payments: ReturnType; + addEmbedListeners: ReturnType; + getActionFiredDetails: ReturnType; + servers: ReturnType; + prisma: typeof prisma; } /** @@ -26,4 +34,19 @@ export const test = base.extend({ const payemntsFixture = createPaymentsFixture(page); await use(payemntsFixture); }, + addEmbedListeners: async ({ page }, use) => { + const embedsFixture = createEmbedsFixture(page); + await use(embedsFixture); + }, + getActionFiredDetails: async ({ page }, use) => { + const getActionFiredDetailsFixture = createGetActionFiredDetails(page); + await use(getActionFiredDetailsFixture); + }, + servers: async ({}, use) => { + const servers = createServersFixture(); + await use(servers); + }, + prisma: async ({}, use) => { + await use(prisma); + }, }); diff --git a/apps/web/playwright-integrations/next-server.ts b/apps/web/playwright/lib/next-server.ts similarity index 100% rename from apps/web/playwright-integrations/next-server.ts rename to apps/web/playwright/lib/next-server.ts diff --git a/apps/web/playwright/login.test.ts b/apps/web/playwright/login.e2e.ts similarity index 100% rename from apps/web/playwright/login.test.ts rename to apps/web/playwright/login.e2e.ts diff --git a/apps/web/playwright/login.oauth.test.ts b/apps/web/playwright/login.oauth.e2e.ts similarity index 82% rename from apps/web/playwright/login.oauth.test.ts rename to apps/web/playwright/login.oauth.e2e.ts index 8913a2fa56..204b8abcf8 100644 --- a/apps/web/playwright/login.oauth.test.ts +++ b/apps/web/playwright/login.oauth.e2e.ts @@ -6,7 +6,7 @@ test("Should display Google Login button", async ({ page }) => { // eslint-disable-next-line playwright/no-skipped-test test.skip(!IS_GOOGLE_LOGIN_ENABLED, "It should only run if Google Login is installed"); - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/auth/login`); + await page.goto(`/auth/login`); await expect(page.locator(`[data-testid=google]`)).toBeVisible(); }); @@ -15,7 +15,7 @@ test("Should display SAML Login button", async ({ page }) => { // eslint-disable-next-line playwright/no-skipped-test test.skip(!IS_SAML_LOGIN_ENABLED, "It should only run if SAML Login is installed"); - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/auth/login`); + await page.goto(`/auth/login`); await expect(page.locator(`[data-testid=saml]`)).toBeVisible(); }); diff --git a/apps/web/playwright/onboarding.test.ts b/apps/web/playwright/onboarding.e2e.ts similarity index 70% rename from apps/web/playwright/onboarding.test.ts rename to apps/web/playwright/onboarding.e2e.ts index 15f6cd0860..89bba6768a 100644 --- a/apps/web/playwright/onboarding.test.ts +++ b/apps/web/playwright/onboarding.e2e.ts @@ -1,23 +1,15 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; import prisma from "@calcom/prisma"; -test.describe("Onboarding", () => { - test.use({ storageState: "playwright/artifacts/onboardingStorageState.json" }); +import { test } from "./lib/fixtures"; - // You want to always reset account completedOnboarding after each test - test.afterEach(async () => { - // Revert DB change - await prisma.user.update({ - where: { - email: "onboarding@example.com", - }, - data: { - username: "onboarding", - completedOnboarding: false, - }, - }); +test.describe("Onboarding", () => { + test.beforeEach(async ({ users }) => { + const onboardingUser = await users.create({ completedOnboarding: false }); + await onboardingUser.login(); }); + test.afterEach(({ users }) => users.deleteAll()); test("redirects to /getting-started after login", async ({ page }) => { await page.goto("/event-types"); @@ -29,10 +21,13 @@ test.describe("Onboarding", () => { }); test.describe("Onboarding", () => { - test("update onboarding username via localstorage", async ({ page }) => { + test("update onboarding username via localstorage", async ({ page, users }) => { + const [onboardingUser] = users.get(); /** + * TODO: * We need to come up with a better test since all test are run in an incognito window. * Meaning that all localstorage access is null here. + * Let's try saving the desiredUsername in the metadata instead */ test.fixme(); await page.addInitScript(() => { @@ -46,7 +41,7 @@ test.describe("Onboarding", () => { await page.waitForTimeout(1000); const updatedUser = await prisma.user.findUnique({ - where: { email: "onboarding@example.com" }, + where: { id: onboardingUser.id }, select: { id: true, username: true }, }); diff --git a/apps/web/playwright/reschedule.test.ts b/apps/web/playwright/reschedule.e2e.ts similarity index 100% rename from apps/web/playwright/reschedule.test.ts rename to apps/web/playwright/reschedule.e2e.ts diff --git a/apps/web/playwright/saml.test.ts b/apps/web/playwright/saml.e2e.ts similarity index 62% rename from apps/web/playwright/saml.test.ts rename to apps/web/playwright/saml.e2e.ts index 2f682af586..565bd1f2df 100644 --- a/apps/web/playwright/saml.test.ts +++ b/apps/web/playwright/saml.e2e.ts @@ -1,12 +1,12 @@ -import { test } from "@playwright/test"; - import { IS_SAML_LOGIN_ENABLED } from "../server/lib/constants"; +import { login } from "./fixtures/users"; +import { test } from "./lib/fixtures"; test.describe("SAML tests", () => { - // Using logged in state from globalSteup - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - test("test SAML configuration UI with pro@example.com", async ({ page }) => { + // TODO: Figure out a way to use the users from fixtures here, right now we cannot set + // the SAML_ADMINS env variables dynamically + await login({ username: "pro", email: "pro@example.com", password: "pro" }, page); // eslint-disable-next-line playwright/no-skipped-test test.skip(!IS_SAML_LOGIN_ENABLED, "It should only run if SAML is enabled"); // Try to go Security page diff --git a/apps/web/playwright/trial.test.ts b/apps/web/playwright/trial.e2e.ts similarity index 100% rename from apps/web/playwright/trial.test.ts rename to apps/web/playwright/trial.e2e.ts diff --git a/apps/web/playwright/wipe-my-cal.test.ts b/apps/web/playwright/wipe-my-cal.e2e.ts similarity index 100% rename from apps/web/playwright/wipe-my-cal.test.ts rename to apps/web/playwright/wipe-my-cal.e2e.ts diff --git a/jest.config.ts b/jest.config.ts index 1b35dfa042..5bd35e14ed 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -50,6 +50,22 @@ const config: Config = { testEnvironment: "jsdom", setupFiles: ["/packages/app-store/closecomothercalendar/test/globals.ts"], }, + { + displayName: "@calcom/api", + roots: ["/apps/api"], + testMatch: ["**/test/lib/**/*.(spec|test).(ts|tsx|js)"], + transform: { + "^.+\\.ts?$": "ts-jest", + }, + transformIgnorePatterns: ["/node_modules/", "^.+\\.module\\.(css|sass|scss)$"], + testEnvironment: "node", + clearMocks: true, + moduleNameMapper: { + "^@lib/(.*)$": "/apps/api/lib/$1", + "^@api/(.*)$": "/apps/api/pages/api/$1", + }, + // setupFilesAfterEnv: ["/apps/api/jest.setup.ts"], // Uncomment when API becomes public + }, ], watchPlugins: [ "jest-watch-typeahead/filename", diff --git a/package.json b/package.json index 7fa2b45542..a5fd0f1997 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,11 @@ "packages/app-store/ee/*" ], "scripts": { - "app-e2e-quick": "turbo run app-e2e-quick", "app-store-cli": "yarn workspace @calcom/app-store-cli", "app-store:build": "yarn app-store-cli build", "app-store:watch": "yarn app-store-cli watch", "app-store": "yarn app-store-cli cli", - "build": "turbo run build --scope=\"@calcom/web\" --include-dependencies", + "build": "turbo run build --filter=@calcom/web...", "clean": "find . -name node_modules -o -name .next -o -name .turbo -o -name dist -type d -prune | xargs rm -rf", "db-deploy": "turbo run db-deploy", "db-seed": "turbo run db-seed", @@ -49,12 +48,13 @@ "prisma": "yarn workspace @calcom/prisma prisma", "start": "turbo run start --scope=\"@calcom/web\"", "tdd": "jest --watch", - "test-e2e": "turbo run test --scope=\"@calcom/web\" && yarn turbo run test-e2e --scope=\"@calcom/web\" --concurrency=1", + "e2e": "NEXT_PUBLIC_IS_E2E=1 yarn playwright test --project=@calcom/web", + "e2e:app-store": "QUICK=true yarn playwright test --project=@calcom/app-store", + "test-e2e": "yarn db-seed && yarn build && yarn e2e", + "test-e2e:app-store": "yarn db-seed && yarn build && yarn e2e:app-store", "test-playwright": "yarn playwright test --config=tests/config/playwright.config.ts", "test": "jest", - "turbo-w": "node turbo-wrapper.js", "type-check": "turbo run type-check", - "test-e2e-integrations": "turbo run test-e2e-integrations --scope=\"@calcom/web\" --concurrency=1", "web": "yarn workspace @calcom/web" }, "devDependencies": { diff --git a/packages/app-store/_apps-playwright/config/globalSetup.ts b/packages/app-store/_apps-playwright/config/globalSetup.ts deleted file mode 100644 index e0478190d2..0000000000 --- a/packages/app-store/_apps-playwright/config/globalSetup.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Page } from "@playwright/test"; - -export async function loginAsUser(username: string, page: Page) { - // Skip if file exists - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/auth/login`); - // Click input[name="email"] - await page.click('input[name="email"]'); - // Fill input[name="email"] - await page.fill('input[name="email"]', `${username}@example.com`); - // Press Tab - await page.press('input[name="email"]', "Tab"); - // Fill input[name="password"] - await page.fill('input[name="password"]', username); - // Press Enter - await page.press('input[name="password"]', "Enter"); - await page.waitForSelector("[data-testid=dashboard-shell]"); - // Save signed-in state to '${username}StorageState.json'. - await page.context().storageState({ path: `playwright/artifacts/${username}StorageState.json` }); -} diff --git a/packages/app-store/_apps-playwright/config/playwright.config.ts b/packages/app-store/_apps-playwright/config/playwright.config.ts deleted file mode 100644 index 0c61ee14df..0000000000 --- a/packages/app-store/_apps-playwright/config/playwright.config.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { PlaywrightTestConfig, devices } from "@playwright/test"; -import { config as dotEnvConfig } from "dotenv"; -import * as path from "path"; - -// TODO: May be derive it automatically, so that moving the file to another location doesn't require changing the code -dotEnvConfig({ path: "../../../../../.env" }); -const DEFAULT_NAVIGATION_TIMEOUT = 15000; - -// Paths are relative to main playwright config. -const outputDir = path.join("../results"); -const testDir = path.join("../tests"); - -// Quick Mode has no retries to fail fast and quickly re-iterate -// Also, it runs the tests only one browser for the same reason -const quickMode = process.env.QUICK === "true"; -const CI = process.env.CI; -export const config: PlaywrightTestConfig = { - forbidOnly: !!CI, - retries: quickMode && !CI ? 0 : 1, - workers: 1, - timeout: 60_000, - reporter: [ - [CI ? "github" : "list"], - [ - "html", - { - outputFolder: path.join(process.cwd(), "playwright", "reports", "playwright-html-report"), - open: "never", - }, - ], - ["junit", { outputFile: path.join(process.cwd(), "playwright", "reports", "results.xml") }], - ], - outputDir, - webServer: { - command: "NEXT_PUBLIC_IS_E2E=1 yarn workspace @calcom/web start -p 3000", - port: 3000, - timeout: 60_000, - reuseExistingServer: !process.env.CI, - }, - use: { - baseURL: "http://localhost:3000", - locale: "en-US", - trace: "retain-on-failure", - headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS, - }, - projects: [ - { - name: "chromium", - testDir, - use: { - ...devices["Desktop Chrome"], - /** If navigation takes more than this, then something's wrong, let's fail fast. */ - navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT, - }, - }, - ], -}; diff --git a/packages/app-store/_apps-playwright/lib/testUtils.ts b/packages/app-store/_apps-playwright/lib/testUtils.ts deleted file mode 100644 index 9bda798242..0000000000 --- a/packages/app-store/_apps-playwright/lib/testUtils.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "../../../../apps/web/playwright/lib/testUtils"; diff --git a/packages/app-store/closecomothercalendar/test/lib/CalendarService.test.ts b/packages/app-store/closecomothercalendar/test/lib/CalendarService.test.ts index ce5c5a9e21..04d9781cdc 100644 --- a/packages/app-store/closecomothercalendar/test/lib/CalendarService.test.ts +++ b/packages/app-store/closecomothercalendar/test/lib/CalendarService.test.ts @@ -5,7 +5,7 @@ import { getCloseComCustomActivityTypeFieldsIds, getCloseComLeadId, } from "@calcom/lib/CloseComeUtils"; -import { CalendarEvent } from "@calcom/types/Calendar"; +import type { CalendarEvent } from "@calcom/types/Calendar"; jest.mock("@calcom/lib/CloseCom", () => ({ default: class { diff --git a/packages/app-store/ee/routing_forms/package.json b/packages/app-store/ee/routing_forms/package.json index 85db623949..3c01527247 100644 --- a/packages/app-store/ee/routing_forms/package.json +++ b/packages/app-store/ee/routing_forms/package.json @@ -5,10 +5,6 @@ "version": "0.0.0", "main": "./index.ts", "description": "It would allow a booker to connect with the right person or choose the right event, faster. It would work by taking inputs from the booker and using that data to route to the correct booker/event as configured by Cal user ", - "scripts": { - "app-e2e": "yarn playwright test --config=playwright/config/playwright.config.ts", - "app-e2e-quick": "QUICK=true yarn app-e2e" - }, "dependencies": { "@calcom/lib": "*", "dotenv": "^16.0.1", diff --git a/packages/app-store/ee/routing_forms/playwright/config/globalSetup.ts b/packages/app-store/ee/routing_forms/playwright/config/globalSetup.ts deleted file mode 100644 index c349ce50b0..0000000000 --- a/packages/app-store/ee/routing_forms/playwright/config/globalSetup.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Page, chromium } from "@playwright/test"; - -// TODO: Import it in _playwright/config/globalSetup.ts and export it from there. -import { loginAsUser } from "@calcom/app-store/_apps-playwright/config/globalSetup"; -import { hashPassword } from "@calcom/lib/auth"; -import prisma from "@calcom/prisma"; - -async function installApp(appName: string, redirectUrl: string, page: Page) { - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/apps/${appName}`); - await page.click('[data-testid="install-app-button"]'); - await page.waitForNavigation({ - url: (url) => { - return url.pathname == redirectUrl; - }, - }); -} - -async function createUser(userName: string) { - const email = `${userName}@example.com`; - await prisma.user.create({ - data: { - username: userName, - email, - completedOnboarding: true, - password: await hashPassword(userName), - }, - }); -} - -async function globalSetup(/* config: FullConfig */) { - const browser = await chromium.launch({ - headless: true, - }); - const page = await browser.newPage(); - const appName = "routing_forms"; - const userName = `${appName}-e2e-${Math.random()}`; - process.env.APP_USER_NAME = userName; - await createUser(userName); - await loginAsUser(userName, page); - await installApp(appName, `/apps/${appName}/forms`, page); - page.context().close(); -} - -export default globalSetup; diff --git a/packages/app-store/ee/routing_forms/playwright/config/globalTeardown.ts b/packages/app-store/ee/routing_forms/playwright/config/globalTeardown.ts deleted file mode 100644 index d279f062e6..0000000000 --- a/packages/app-store/ee/routing_forms/playwright/config/globalTeardown.ts +++ /dev/null @@ -1,18 +0,0 @@ -import prisma from "@calcom/prisma"; - -async function deleteUser(userName: string) { - await prisma.user.deleteMany({ - where: { - AND: { - username: { - contains: userName, - }, - }, - }, - }); -} -async function globalTeardown(/* config: FullConfig */) { - await deleteUser("routing_forms-e2e"); -} - -export default globalTeardown; diff --git a/packages/app-store/ee/routing_forms/playwright/config/playwright.config.ts b/packages/app-store/ee/routing_forms/playwright/config/playwright.config.ts deleted file mode 100644 index 85cadbc46f..0000000000 --- a/packages/app-store/ee/routing_forms/playwright/config/playwright.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { expect, Config } from "@playwright/test"; - -import { config as baseConfig } from "@calcom/app-store/_apps-playwright/config/playwright.config"; - -const config: Config = { - ...baseConfig, - globalSetup: require.resolve("./globalSetup"), - globalTeardown: require.resolve("./globalTeardown"), -}; - -expect.extend({}); -export default config; diff --git a/packages/app-store/ee/routing_forms/playwright/fixtures/fixtures.ts b/packages/app-store/ee/routing_forms/playwright/fixtures/fixtures.ts deleted file mode 100644 index aa69d0192a..0000000000 --- a/packages/app-store/ee/routing_forms/playwright/fixtures/fixtures.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { test as base } from "@playwright/test"; - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface Fixtures {} -export const test = base.extend({}); diff --git a/packages/app-store/ee/routing_forms/playwright/lib/testUtils.ts b/packages/app-store/ee/routing_forms/playwright/lib/testUtils.ts deleted file mode 100644 index 94b7026536..0000000000 --- a/packages/app-store/ee/routing_forms/playwright/lib/testUtils.ts +++ /dev/null @@ -1,20 +0,0 @@ -import prisma from "@calcom/prisma"; - -export * from "@calcom/app-store/_apps-playwright/lib/testUtils"; -export async function cleanUpForms() { - await prisma.app_RoutingForms_Form.deleteMany({ - where: { - user: { - username: process.env.APP_USER_NAME, - }, - }, - }); -} - -export async function cleanUpSeededForm(formId: string) { - return await prisma.app_RoutingForms_FormResponse.deleteMany({ - where: { - formId, - }, - }); -} diff --git a/packages/app-store/ee/routing_forms/playwright/tests/basic.test.ts b/packages/app-store/ee/routing_forms/playwright/tests/basic.e2e.ts similarity index 93% rename from packages/app-store/ee/routing_forms/playwright/tests/basic.test.ts rename to packages/app-store/ee/routing_forms/playwright/tests/basic.e2e.ts index 5de27705d2..1b708a70d1 100644 --- a/packages/app-store/ee/routing_forms/playwright/tests/basic.test.ts +++ b/packages/app-store/ee/routing_forms/playwright/tests/basic.e2e.ts @@ -1,9 +1,7 @@ import { expect, Page } from "@playwright/test"; import { seededForm } from "@calcom/prisma/seed-app-store"; - -import { test } from "../fixtures/fixtures"; -import { cleanUpForms, cleanUpSeededForm } from "../lib/testUtils"; +import { test } from "@calcom/web/playwright/lib/fixtures"; async function gotoRoutingLink(page: Page, formId: string) { await page.goto(`/forms/${formId}`); @@ -69,12 +67,24 @@ async function fillForm( }; } -test.use({ storageState: `playwright/artifacts/${process.env.APP_USER_NAME}StorageState.json` }); test.describe("Routing Forms", () => { - test("should be able to add a new form and view it", async ({ page }) => { - page.goto("/"); + test.beforeEach(async ({ page, users }) => { + const user = await users.create({ username: "routing_forms" }); + await user.login(); + // Install app + await page.goto(`/apps/routing_forms`); + await page.click('[data-testid="install-app-button"]'); + await page.waitForNavigation({ + url: (url) => url.pathname === `/apps/routing_forms/forms`, + }); + }); - await page.click('[href="/apps/routing_forms/forms"]'); + test.afterEach(async ({ users }) => { + // This also delete forms on cascade + await users.deleteAll(); + }); + + test("should be able to add a new form and view it", async ({ page }) => { await page.waitForSelector('[data-testid="empty-screen"]'); const formId = await addForm(page); @@ -98,8 +108,6 @@ test.describe("Routing Forms", () => { }); test("should be able to edit the form", async ({ page }) => { - await page.goto("/apps/routing_forms/forms"); - await addForm(page); const description = "Test Description"; @@ -218,12 +226,4 @@ test.describe("Routing Forms", () => { expect(await page.locator('button[type="submit"][disabled]').count()).toBe(0); }); }); - - test.afterAll(() => { - cleanUpForms(); - }); - - test.afterEach(() => { - cleanUpSeededForm(seededForm.id); - }); }); diff --git a/packages/embeds/embed-core/playwright/tests/inline.test.ts b/packages/embeds/embed-core/playwright/tests/inline.e2e.ts similarity index 82% rename from packages/embeds/embed-core/playwright/tests/inline.test.ts rename to packages/embeds/embed-core/playwright/tests/inline.e2e.ts index 90430887e9..a553556f3f 100644 --- a/packages/embeds/embed-core/playwright/tests/inline.test.ts +++ b/packages/embeds/embed-core/playwright/tests/inline.e2e.ts @@ -1,7 +1,8 @@ -import { expect, Frame } from "@playwright/test"; +import { expect } from "@playwright/test"; -import { test } from "../fixtures/fixtures"; -import { todo, getEmbedIframe, bookFirstEvent, deleteAllBookingsByEmail } from "../lib/testUtils"; +import { test } from "@calcom/web/playwright/lib/fixtures"; + +import { bookFirstEvent, deleteAllBookingsByEmail, getEmbedIframe, todo } from "../lib/testUtils"; test("Inline Iframe - Configured with Dark Theme", async ({ page, diff --git a/packages/embeds/embed-core/playwright/tests/preview.test.ts b/packages/embeds/embed-core/playwright/tests/preview.e2e.ts similarity index 90% rename from packages/embeds/embed-core/playwright/tests/preview.test.ts rename to packages/embeds/embed-core/playwright/tests/preview.e2e.ts index abe63ef965..ea21cab2ba 100644 --- a/packages/embeds/embed-core/playwright/tests/preview.test.ts +++ b/packages/embeds/embed-core/playwright/tests/preview.e2e.ts @@ -1,6 +1,6 @@ import { expect } from "@playwright/test"; -import { test } from "../fixtures/fixtures"; +import { test } from "@calcom/web/playwright/lib/fixtures"; test("Preview - embed-core should load", async ({ page }) => { await page.goto("http://localhost:3000/embed/preview.html"); diff --git a/packages/lib/CloseComeUtils.ts b/packages/lib/CloseComeUtils.ts index 83f1a59350..386f852883 100644 --- a/packages/lib/CloseComeUtils.ts +++ b/packages/lib/CloseComeUtils.ts @@ -1,4 +1,4 @@ -import { CalendarEvent } from "@calcom/types/Calendar"; +import type { CalendarEvent } from "@calcom/types/Calendar"; import CloseCom, { CloseComCustomActivityCreate, diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000000..85f2d6f142 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,96 @@ +import { devices, PlaywrightTestConfig } from "@playwright/test"; +import dotEnv from "dotenv"; +import * as os from "os"; +import * as path from "path"; + +dotEnv.config({ path: ".env" }); + +const outputDir = path.join(__dirname, "test-results"); +const testDir = path.join(__dirname, "apps/web/playwright"); + +const DEFAULT_NAVIGATION_TIMEOUT = 15000; + +const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS; + +const IS_EMBED_TEST = process.argv.some((a) => a.startsWith("--project=@calcom/embed-core")); + +const webServer: PlaywrightTestConfig["webServer"] = [ + { + command: "NEXT_PUBLIC_IS_E2E=1 yarn workspace @calcom/web start -p 3000", + port: 3000, + timeout: 60_000, + reuseExistingServer: !process.env.CI, + }, +]; + +if (IS_EMBED_TEST) { + webServer.push({ + command: "yarn workspace @calcom/embed-core run-p 'embed-dev' 'embed-web-start'", + port: 3100, + timeout: 60_000, + reuseExistingServer: !process.env.CI, + }); +} + +const config: PlaywrightTestConfig = { + forbidOnly: !!process.env.CI, + retries: 1, + workers: os.cpus().length, + timeout: 60_000, + maxFailures: headless ? 10 : undefined, + reporter: [ + [process.env.CI ? "github" : "list"], + ["html", { outputFolder: "./test-results/reports/playwright-html-report", open: "never" }], + ["junit", { outputFile: "./test-results/reports/results.xml" }], + ], + outputDir: path.join(outputDir, "results"), + webServer, + use: { + baseURL: "http://localhost:3000/", + locale: "en-US", + trace: "retain-on-failure", + headless, + }, + projects: [ + { + name: "@calcom/web", + testDir: "./apps/web/playwright", + testMatch: /.*\.e2e\.tsx?/, + use: { + ...devices["Desktop Chrome"], + /** If navigation takes more than this, then something's wrong, let's fail fast. */ + navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT, + }, + }, + { + name: "@calcom/app-store", + testDir: "./packages/app-store/", + testMatch: /.*\.e2e\.tsx?/, + use: { + ...devices["Desktop Chrome"], + /** If navigation takes more than this, then something's wrong, let's fail fast. */ + navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT, + }, + }, + { + name: "@calcom/embed-core", + testDir: "./packages/embeds/", + testMatch: /.*\.e2e\.tsx?/, + use: { ...devices["Desktop Chrome"] }, + }, + { + name: "@calcom/embed-core--firefox", + testDir: "./packages/embeds/", + testMatch: /.*\.e2e\.tsx?/, + use: { ...devices["Desktop Firefox"] }, + }, + { + name: "@calcom/embed-core--webkit", + testDir: "./packages/embeds/", + testMatch: /.*\.e2e\.tsx?/, + use: { ...devices["Desktop Safari"] }, + }, + ], +}; + +export default config; diff --git a/tests/config/globalSetup.ts b/tests/config/globalSetup.ts deleted file mode 100644 index 289db77983..0000000000 --- a/tests/config/globalSetup.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { loadEnvConfig } from "@next/env"; -import { Browser, chromium } from "@playwright/test"; -import fs from "fs"; - -export async function loginAsUser(username: string, browser: Browser) { - // Skip is file exists - if (fs.existsSync(`playwright/artifacts/${username}StorageState.json`)) return; - const page = await browser.newPage(); - await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/auth/login`); - // Click input[name="email"] - await page.click('input[name="email"]'); - // Fill input[name="email"] - await page.fill('input[name="email"]', `${username}@example.com`); - // Press Tab - await page.press('input[name="email"]', "Tab"); - // Fill input[name="password"] - await page.fill('input[name="password"]', username); - // Press Enter - await page.press('input[name="password"]', "Enter"); - await page.waitForSelector( - username === "onboarding" ? "[data-testid=onboarding]" : "[data-testid=dashboard-shell]" - ); - // Save signed-in state to '${username}StorageState.json'. - await page.context().storageState({ path: `playwright/artifacts/${username}StorageState.json` }); - await page.context().close(); -} - -async function globalSetup(/* config: FullConfig */) { - loadEnvConfig(process.env.PWD); - const browser = await chromium.launch(); - - await loginAsUser("onboarding", browser); - // await loginAsUser("free-first-hidden", browser); - await loginAsUser("pro", browser); - await loginAsUser("trial", browser); - await loginAsUser("free", browser); - // await loginAsUser("usa", browser); - // await loginAsUser("teamfree", browser); - await loginAsUser("teampro", browser); - await browser.close(); - // Clean up auth state after all tests are done - return () => { - const dir = `playwright/artifacts`; - fs.readdirSync(dir).forEach((f) => fs.rmSync(`${dir}/${f}`)); - }; -} - -export default globalSetup; diff --git a/tests/config/playwright.config.ts b/tests/config/playwright.config.ts deleted file mode 100644 index 69d7b88e32..0000000000 --- a/tests/config/playwright.config.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { devices, PlaywrightTestConfig } from "@playwright/test"; -import dotEnv from "dotenv"; -import * as os from "os"; -import * as path from "path"; - -dotEnv.config({ path: "../../.env" }); - -const outputDir = path.join(__dirname, "..", "..", "test-results"); -const testDir = path.join(__dirname, "..", "..", "apps/web/playwright"); - -const DEFAULT_NAVIGATION_TIMEOUT = 15000; - -const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS; - -const config: PlaywrightTestConfig = { - forbidOnly: !!process.env.CI, - retries: 1, - workers: os.cpus().length, - timeout: 60_000, - maxFailures: headless ? 10 : undefined, - reporter: [ - [process.env.CI ? "github" : "list"], - ["html", { outputFolder: path.join(outputDir, "reports/playwright-html-report"), open: "never" }], - ["junit", { outputFile: path.join(outputDir, "reports/results.xml") }], - ], - globalSetup: require.resolve("./globalSetup"), - outputDir: path.join(outputDir, "results"), - webServer: { - command: "NEXT_PUBLIC_IS_E2E=1 yarn workspace @calcom/web start -p 3000", - port: 3000, - timeout: 60_000, - reuseExistingServer: !process.env.CI, - }, - use: { - baseURL: "http://localhost:3000/", - locale: "en-US", - trace: "retain-on-failure", - headless, - }, - projects: [ - { - name: "chromium", - testDir, - use: { - ...devices["Desktop Chrome"], - /** If navigation takes more than this, then something's wrong, let's fail fast. */ - navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT, - }, - }, - /* { - name: "firefox", - use: { ...devices["Desktop Firefox"] }, - }, - { - name: "webkit", - use: { ...devices["Desktop Safari"] }, - }, */ - ], -}; - -export default config; diff --git a/turbo.json b/turbo.json index f28746fc4f..0ecc235b9c 100644 --- a/turbo.json +++ b/turbo.json @@ -1,6 +1,5 @@ { "$schema": "https://turborepo.org/schema.json", - "baseBranch": "origin/main", "pipeline": { "@calcom/prisma#build": { "cache": false, @@ -123,24 +122,6 @@ "embed-tests-quick": { "cache": false }, - "test": { - "dependsOn": ["^test"] - }, - "@calcom/web#db-setup-tests": { - "cache": false - }, - "@calcom/web#test": { - "cache": false, - "dependsOn": ["@calcom/web#db-setup-tests"] - }, - "test-e2e": { - "cache": false, - "dependsOn": ["@calcom/prisma#db-seed", "@calcom/web#build"] - }, - "test-e2e-integrations": { - "cache": false, - "dependsOn": ["@calcom/prisma#db-seed", "@calcom/web#build"] - }, "type-check": { "cache": false, "outputs": [] @@ -170,10 +151,6 @@ "^embed-tests-update-snapshots:ci" ] }, - "app-e2e-quick": { - "cache": false, - "dependsOn": ["@calcom/prisma#db-seed", "@calcom/web#build", "^app-e2e-quick"] - }, "//#env-check:common": { "cache": false, "inputs": ["./.env.example", "./.env"], @@ -183,6 +160,10 @@ "cache": false, "inputs": ["./.env.appStore.example", "./.env.appStore"], "outputs": ["./.env.appStore"] + }, + "//#test": { + "cache": false, + "outputs": [] } }, "globalDependencies": [ diff --git a/yarn.lock b/yarn.lock index 2f08feb5bf..cfb219615b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2410,17 +2410,12 @@ resolved "https://registry.yarnpkg.com/@glidejs/glide/-/glide-3.5.2.tgz#7012c5920ecf202bbda44d8526fc979984b6dd54" integrity sha512-7jGciNJ2bQ4eZLSNlSZ+VAyW63kALf420CvkEpK4lEsUfWJq9odqimci0YCiyNyMUFB+pWHwLYyNc57dijYsCg== -"@headlessui/react@^1.4.1": - version "1.6.6" - resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.6.tgz#3073c066b85535c9d28783da0a4d9288b5354d0c" - integrity sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q== - "@headlessui/react@^1.5.0": version "1.6.5" resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.5.tgz#5587c537de809cf3146eb2ff263e5e940b1bf69c" integrity sha512-3VkKteDxlxf3fE0KbfO9t60KC1lM7YNpZggLpwzVNg1J/zwL+h+4N7MBlFDVpInZI3rKlZGpNx0PWsG/9c2vQg== -"@heroicons/react@^1.0.4", "@heroicons/react@^1.0.6": +"@heroicons/react@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324" integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ== @@ -3770,17 +3765,6 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-slot" "0.1.2" -"@radix-ui/react-collection@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.0.tgz#0ec4c72fabd35a03b5787075ac799e3b17ca5710" - integrity sha512-8i1pf5dKjnq90Z8udnnXKzdCEV3/FYrfw0n/b6NvB6piXEn3fO1bOh7HBcpG8XrnIXzxlYu2oCcR38QpyLS/mg== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-compose-refs" "1.0.0" - "@radix-ui/react-context" "1.0.0" - "@radix-ui/react-primitive" "1.0.0" - "@radix-ui/react-slot" "1.0.0" - "@radix-ui/react-compose-refs@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz#cff6e780a0f73778b976acff2c2a5b6551caab95" @@ -3830,13 +3814,6 @@ aria-hidden "^1.1.1" react-remove-scroll "^2.4.0" -"@radix-ui/react-direction@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.0.tgz#a2e0b552352459ecf96342c79949dd833c1e6e45" - integrity sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-dismissable-layer@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.1.5.tgz#9379032351e79028d472733a5cc8ba4a0ea43314" @@ -3984,7 +3961,7 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-use-layout-effect" "0.1.0" -"@radix-ui/react-portal@1.0.0", "@radix-ui/react-portal@^1.0.0": +"@radix-ui/react-portal@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b" integrity sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA== @@ -4058,22 +4035,6 @@ "@radix-ui/react-use-callback-ref" "0.1.0" "@radix-ui/react-use-controllable-state" "0.1.0" -"@radix-ui/react-roving-focus@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.0.tgz#aadeb65d5dbcdbdd037078156ae1f57c2ff754ee" - integrity sha512-lHvO4MhvoWpeNbiJAoyDsEtbKqP2jkkdwsMVJ3kfqbkC71J/aXE6Th6gkZA1xHEqSku+t+UgoDjvE7Z3gsBpcg== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.0" - "@radix-ui/react-collection" "1.0.0" - "@radix-ui/react-compose-refs" "1.0.0" - "@radix-ui/react-context" "1.0.0" - "@radix-ui/react-direction" "1.0.0" - "@radix-ui/react-id" "1.0.0" - "@radix-ui/react-primitive" "1.0.0" - "@radix-ui/react-use-callback-ref" "1.0.0" - "@radix-ui/react-use-controllable-state" "1.0.0" - "@radix-ui/react-select@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-0.1.1.tgz#ceedea6856a37e4079492e1c69601797cedd2c85" @@ -4099,7 +4060,7 @@ aria-hidden "^1.1.1" react-remove-scroll "^2.4.0" -"@radix-ui/react-slider@^0.1.0", "@radix-ui/react-slider@^0.1.1": +"@radix-ui/react-slider@^0.1.1": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-0.1.4.tgz#a7b7a480ee00158195794b08cd3f1583cf102518" integrity sha512-0z3bCcdrAi+FIcoLXS6r0ESVWuuyMnUJoCsFm7tC7Rtv95x34YtaI8YfSyQmzuMVS4rTsNtCCTZ/s727uRaVkQ== @@ -4148,21 +4109,6 @@ "@radix-ui/react-use-previous" "0.1.1" "@radix-ui/react-use-size" "0.1.1" -"@radix-ui/react-tabs@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.0.tgz#135c67f1f2bd9ada69a3f6e38dd897d459af5fe5" - integrity sha512-oKUwEDsySVC0uuSEH7SHCVt1+ijmiDFAI9p+fHCtuZdqrRDKIFs09zp5nrmu4ggP6xqSx9lj1VSblnDH+n3IBA== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.0" - "@radix-ui/react-context" "1.0.0" - "@radix-ui/react-direction" "1.0.0" - "@radix-ui/react-id" "1.0.0" - "@radix-ui/react-presence" "1.0.0" - "@radix-ui/react-primitive" "1.0.0" - "@radix-ui/react-roving-focus" "1.0.0" - "@radix-ui/react-use-controllable-state" "1.0.0" - "@radix-ui/react-toggle-group@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-0.1.5.tgz#9e4d65e22c4fc0ba3a42fbc8d5496c430e5e9852" @@ -5746,7 +5692,7 @@ dependencies: prop-types "^15.7.2" -"@stripe/stripe-js@^1.17.1", "@stripe/stripe-js@^1.35.0": +"@stripe/stripe-js@^1.34.0", "@stripe/stripe-js@^1.35.0": version "1.35.0" resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.35.0.tgz#f809e2e5e0a00f01aa12e8aed0b89d27728c05c0" integrity sha512-UIuzpbJqgXCTvJhY/aZYvBtaKdMfQgnIv6kkLlfRJ9smZcC4zoPvq3j7k9wobYI+idHAWP4BRiPnqA8lvzJCtg== @@ -6373,16 +6319,11 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@16.9.1", "@types/node@>=12.0.0", "@types/node@>=8.1.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": +"@types/node@*", "@types/node@16.9.1", "@types/node@>=12.0.0", "@types/node@>=8.1.0", "@types/node@^12.12.6", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": version "16.9.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== -"@types/node@^12.12.6": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== - "@types/nodemailer@^6.4.5": version "6.4.5" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.5.tgz#09011ac73259245475d1688e4ba101860567dc39" @@ -6511,7 +6452,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16", "@types/react@^18.0.17": +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16", "@types/react@^18.0.17", "@types/react@^18.0.9": version "18.0.17" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.17.tgz#4583d9c322d67efe4b39a935d223edcc7050ccf4" integrity sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ== @@ -6520,24 +6461,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@18.0.9": - version "18.0.9" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" - integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^18.0.9": - version "18.0.18" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.18.tgz#9f16f33d57bc5d9dca848d12c3572110ff9429ac" - integrity sha512-6hI08umYs6NaiHFEEGioXnxJ+oEhY3eRz8VCUaudZmGdtvPviCJB8mgaMxaDWAdPSYd4eFavrPk2QIolwbLYrg== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -7846,7 +7769,7 @@ autolinker@^3.11.0: dependencies: tslib "^2.3.0" -autoprefixer@^10.3.4, autoprefixer@^10.4.7, autoprefixer@^10.4.8: +autoprefixer@^10.4.7, autoprefixer@^10.4.8: version "10.4.8" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.8.tgz#92c7a0199e1cfb2ad5d9427bd585a3d75895b9e5" integrity sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw== @@ -10220,11 +10143,6 @@ denque@^2.0.1: resolved "https://registry.yarnpkg.com/denque/-/denque-2.0.1.tgz#bcef4c1b80dc32efe97515744f21a4229ab8934a" integrity sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ== -depd@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" - integrity sha512-Jlk9xvkTDGXwZiIDyoM7+3AsuvJVoyOpRupvEVy9nX3YO3/ieZxhlgh8GpLNZ8AY7HjO6y2YwpMSh1ejhu3uIw== - depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -11301,7 +11219,7 @@ eslint@8.4.1: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^8.15.0, eslint@^8.20.0: +eslint@^8.20.0: version "8.23.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.0.tgz#a184918d288820179c6041bb3ddcc99ce6eea040" integrity sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA== @@ -13072,11 +12990,6 @@ gray-matter@^4.0.3: section-matter "^1.0.0" strip-bom-string "^1.0.0" -gsap@^3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/gsap/-/gsap-3.11.0.tgz#5a45da0f35b49e2c479e1e274feb6ac51e86478b" - integrity sha512-TV5aFGqXht+0o/CelnhCikSe3QGeG+q1XA/fyFFsMzesILHgWgFWIz0NuXIgcMaL5h7MG2l+j0BTupS5YyYkrw== - gtoken@^5.0.4: version "5.3.2" resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" @@ -13546,16 +13459,6 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" - integrity sha512-STnYGcKMXL9CGdtpeTFnLmgMSHTTNQJSHxiC4DETHKf934Q160Ht5pljrNeH24S0O9xUN+9vsDJZdZtk5js6Ww== - dependencies: - depd "1.1.1" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - http-errors@1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" @@ -13684,11 +13587,6 @@ ical.js@^1.4.0: resolved "https://registry.yarnpkg.com/ical.js/-/ical.js-1.5.0.tgz#23213accd1d8f7248d01705acb06270a70d20662" integrity sha512-7ZxMkogUkkaCx810yp0ZGKvq1ZpRgJeornPttpoxe6nYZ3NLesZe1wWMXDdwTkj/b5NtXT+Y16Aakph/ao98ZQ== -iconv-lite@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -14439,7 +14337,7 @@ is-shared-array-buffer@^1.0.1, is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" -is-stream@1.1.0, is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -16554,17 +16452,7 @@ methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micro@9.3.4: - version "9.3.4" - resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.4.tgz#745a494e53c8916f64fb6a729f8cbf2a506b35ad" - integrity sha512-smz9naZwTG7qaFnEZ2vn248YZq9XR+XoOH3auieZbkhDL4xLOxiE+KqG8qqnBeKfXA9c1uEFGCxPN1D+nT6N7w== - dependencies: - arg "4.1.0" - content-type "1.0.4" - is-stream "1.1.0" - raw-body "2.3.2" - -micro@^9.4.1: +micro@^9.4.0, micro@^9.4.1: version "9.4.1" resolved "https://registry.yarnpkg.com/micro/-/micro-9.4.1.tgz#3a7eedd96718d8569a324475cd1967441df4b3c7" integrity sha512-Lpjcbp6Y9GJIfewxDfTmu9eW0rt0MGo+Gs1d3yJLFa7mhErtKkCngGhDbA/O1gqUjEwsHh+jWPg8BJ0Bx4AgFA== @@ -17524,7 +17412,7 @@ next-validations@^0.2.0: resolved "https://registry.yarnpkg.com/next-validations/-/next-validations-0.2.1.tgz#68010c9b017ba48eec4f404fd42eb9b0c7324737" integrity sha512-92pR14MPTTx0ynlvYH2TwMf7WiGiznNL/l0dtZyKPw3x48rcMhwEZrP1ZmsMJwzp5D+U+sY2deexeLWC8rlNtQ== -next@^12.2.0, next@^12.2.5: +next@^12.2.5: version "12.2.5" resolved "https://registry.yarnpkg.com/next/-/next-12.2.5.tgz#14fb5975e8841fad09553b8ef41fe1393602b717" integrity sha512-tBdjqX5XC/oFs/6gxrZhjmiq90YWizUYU6qOWAfat7zJwrwapJ+BYgX2PmiacunXMaRpeVT4vz5MSPSLgNkrpA== @@ -19478,16 +19366,6 @@ range-parser@^1.2.0, range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" - integrity sha512-Ss0DsBxqLxCmQkfG5yazYhtbVVTJqS9jTsZG2lhrNwqzOk2SUC7O/NB/M//CkEBqsrtmlNgJCPccJGuYSFr6Vg== - dependencies: - bytes "3.0.0" - http-errors "1.6.2" - iconv-lite "0.4.19" - unpipe "1.0.0" - raw-body@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" @@ -19687,11 +19565,6 @@ react-element-to-jsx-string@^14.3.4: is-plain-object "5.0.0" react-is "17.0.2" -react-fast-marquee@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/react-fast-marquee/-/react-fast-marquee-1.3.5.tgz#e53995027102fbec92da90606d7ca89703db9903" - integrity sha512-eOqLoz4iVVBvi2wN/web8hd2XX9y2Z6CYR7g++7nTVHlTOXBtqyARQJ9rYNpbp179hAzloMx0yBFAo8LpNYmKQ== - react-feather@^2.0.10: version "2.0.10" resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.10.tgz#0e9abf05a66754f7b7bb71757ac4da7fb6be3b68" @@ -19713,12 +19586,12 @@ react-gtm-module@^2.0.11: resolved "https://registry.yarnpkg.com/react-gtm-module/-/react-gtm-module-2.0.11.tgz#14484dac8257acd93614e347c32da9c5ac524206" integrity sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw== -react-hook-form@^7.31.1, react-hook-form@^7.34.2: +react-hook-form@^7.33.1, react-hook-form@^7.34.2: version "7.34.2" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.34.2.tgz#9ac6d1a309a7c4aaa369d1269357a70e9e9bf4de" integrity sha512-1lYWbEqr0GW7HHUjMScXMidGvV0BE2RJV3ap2BL7G0EJirkqpccTaawbsvBO8GZaB3JjCeFBEbnEWI1P8ZoLRQ== -react-hot-toast@^2.1.1, react-hot-toast@^2.3.0: +react-hot-toast@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.3.0.tgz#70b3d183ac2a4afb6b17cda4a7f4cfe02e730415" integrity sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA== @@ -19800,11 +19673,6 @@ react-live-chat-loader@^2.7.3: resolved "https://registry.yarnpkg.com/react-live-chat-loader/-/react-live-chat-loader-2.7.3.tgz#a66a7d64eacdf0a680570b4e9a99639a88bffaae" integrity sha512-VviVqnF3PYDBJ77JiPZv4cpulx806L22WfsIQxvlxlEWmzKNp/0lEs57uP6EJcE+d1jQwGcR9DIsj5qouE6OkA== -react-masonry-css@^1.0.16: - version "1.0.16" - resolved "https://registry.yarnpkg.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz#72b28b4ae3484e250534700860597553a10f1a2c" - integrity sha512-KSW0hR2VQmltt/qAa3eXOctQDyOu7+ZBevtKgpNDSzT7k5LA/0XntNa9z9HKCdz3QlxmJHglTZ18e4sX4V8zZQ== - react-merge-refs@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" @@ -21079,11 +20947,6 @@ setimmediate@^1.0.4, setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - integrity sha512-9jphSf3UbIgpOX/RKvX02iw/rN2TKdusnsPpGfO/rkcsrd+IRqgHZb4VGnmL0Cynps8Nj2hN45wsi30BzrHDIw== - setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" @@ -21523,7 +21386,7 @@ statuses@2.0.1, statuses@^2.0.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.3.1 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -21870,7 +21733,7 @@ stripe@*: "@types/node" ">=8.1.0" qs "^6.10.3" -stripe@^9.1.0, stripe@^9.16.0: +stripe@^9.16.0: version "9.16.0" resolved "https://registry.yarnpkg.com/stripe/-/stripe-9.16.0.tgz#94c24549c91fced457b9e3259e8a1a1bdb6dbd0e" integrity sha512-Dn8K+jSoQcXjxCobRI4HXUdHjOXsiF/KszK49fJnkbeCFjZ3EZxLG2JiM/CX+Hcq27NBDtv/Sxhvy+HhTmvyaQ== @@ -22744,71 +22607,155 @@ turbo-android-arm64@1.4.3: resolved "https://registry.yarnpkg.com/turbo-android-arm64/-/turbo-android-arm64-1.4.3.tgz#d531c6935134d8cae9f31f61db47d13bd227bb93" integrity sha512-ZUvdoEHJkTkOFOO9PKWYrdONDBVqkNsvwEMufTVf07RXgqmbXDPkznzT4hcQm6xXyqWqJdjgSAMdlm+2nNE1Og== +turbo-darwin-64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.2.9.tgz#001159794757f77c4b016fc56d045c5e7bfc9510" + integrity sha512-rVwDQpi6p0GwTiqSsvtA1b3RvKl8l2y+ElZ3EKGiIIJYZt1D6wBMJoADaZ9uZ/LWkT+WKfAWNtKdwRmuBAOS6g== + turbo-darwin-64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.4.3.tgz#123e214d9070ec6ac81468dc7a1cb02a459fafd7" integrity sha512-gapoVm5qbu2TJS4lJ6fM3o2eAkLyXSxHihw/4NRAYmwHCH3at1/cIAnRcctB/HLL3ZaB/p3HKb8mnI7k6xNHOw== +turbo-darwin-arm64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.2.9.tgz#fb19615984d1780fdcdd9e54a607efb46a700e72" + integrity sha512-j7NgQHkQWWODw1I/saiqmjjD54uGAEq0qTTtLI3RoLaA+yI+awXmHwsiHRqsvGSyGJlBoKBcbxXkekLf21q3GA== + turbo-darwin-arm64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.4.3.tgz#9fa062c3ffa9208d0e2fa0155dcb48a290e7c109" integrity sha512-XUe6FTsHamEH7FfNslYYO04yecAaguhZuwW4kE9B/BAP8MUYsmVqONauLPyE/YqM6pf2K0xwVe+RlEGf53CWbg== +turbo-freebsd-64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-freebsd-64/-/turbo-freebsd-64-1.2.9.tgz#edb2ee840ba80c257068e339d10ac460c1f9fb10" + integrity sha512-+tLb3iCOrIeGrcOJZYey5mD9qgNgKYuwRRg6FeX/6TDITvZXcCS50A2uRbaD/PQzQKs1lHcshiCe/DRtbvJ63g== + turbo-freebsd-64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-freebsd-64/-/turbo-freebsd-64-1.4.3.tgz#ffb4a939ca0000ec91114d2bbddc491c4835e862" integrity sha512-1CAjXmDClgMXdWZXreUfAbGBB2WB9TZHfJIdsgnDqt4fIcFGChknzYqc+Fj3tGHAczMpinGjBbWIzFuxOq/ofQ== +turbo-freebsd-arm64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-freebsd-arm64/-/turbo-freebsd-arm64-1.2.9.tgz#aebfb5b07a2dd6e9211fb6d9827c2f2288e2ca73" + integrity sha512-gwI8jocTf036kc9GI1BebzftxrkT5pewHPA2iqvAXAJpX01G1x1iGcl8/uIbkbL5hp038nu+l2Kb+lRI96sJuA== + turbo-freebsd-arm64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-freebsd-arm64/-/turbo-freebsd-arm64-1.4.3.tgz#0bab3e2ccfac1ecc9ed9a30b0ab80f379fe3a788" integrity sha512-j5C7j/vwabPKpr5d6YlLgHGHBZCOcXj3HdkBshDHTQ0wghH0NuCUUaesYxI3wva/4/Ec0dhIrb20Laa/HMxXLA== +turbo-linux-32@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-linux-32/-/turbo-linux-32-1.2.9.tgz#1462e45776d3ce93c57f2014745e7edb60910a44" + integrity sha512-Rm47bIsCHIae/DkXJ58YrWvdh8o4Ug9U4VnTDb9byXrz2B7624ol9XdfpXv429z7LXkQR1+WnwCMwFB4K6DyuQ== + turbo-linux-32@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-linux-32/-/turbo-linux-32-1.4.3.tgz#96deb5ebedfe6b97fd8ecc0fa40ff1c891c4c04f" integrity sha512-vnc+StXIoQEnxIU43j7rEz/J+v+RV4dbUdUolBq0k9gkUV8KMCcqPkIa753K47E2KLNGKXMaYDI6AHQX1GAQZg== +turbo-linux-64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.2.9.tgz#0eef4a6f6f267b773ea58c4bda86922092eda3eb" + integrity sha512-8Gqi+TzEdmOmxxAukU0NO0JlIqdm98C97u9qEsxWrXTFL/xL21gKCixqsBTEO7JOISC4M8VjArxjSsITRbkD5g== + turbo-linux-64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.4.3.tgz#ed0152e724c78dea090b2d60d29258110bdfb14a" integrity sha512-KAUeIa8Ejt6BLrBGbVurlrjDxqh62tu75D4cqKqKfzWspcbEtmdqlV6qthXfm8SlzGSNuQXX0+qXEWds2FIZXg== +turbo-linux-arm64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.2.9.tgz#57777d356bf74ef2cc284998ef86fe19c77b92c2" + integrity sha512-FVIeM7koUtyu1cNAJhPYjb90kL/ICdWoJr4PoZZYnqty5sxLsBg75bVErEDQeDzKQvwXLlcax2lEzHvaSyn/wg== + turbo-linux-arm64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.4.3.tgz#74fac47575e6b5af5830d26a56493859d09e4c7c" integrity sha512-rzB7w+RHCQkKr8aDxxozv/IzdN976CYyBiRocSf9QGU73uyAg8pCo3i0MiENSRjDC+tUbdbu2lEUwGXf9ziB9Q== +turbo-linux-arm@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-linux-arm/-/turbo-linux-arm-1.2.9.tgz#2424016fb926143682f9029bd86a8d455979e075" + integrity sha512-OS+XCWiGFbuM7UNBVQdVbIJqxhVu9Sr2WxQgDcGZpCYn32yLLPlWDDGL0Cl/EG006J9k+VS1e4OzyM6kfMxS9Q== + turbo-linux-arm@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-linux-arm/-/turbo-linux-arm-1.4.3.tgz#ac42b3c5918fe06270cb8dfbe31442a48cc38fc3" integrity sha512-zZNoHUK5ioFyxAngh8tHe763Dzb22ne3LJkaZn0ExkFHJtWClWv536lPcDuQPpIH9W9iz5OwPKtN32DNpNwk8A== +turbo-linux-mips64le@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-linux-mips64le/-/turbo-linux-mips64le-1.2.9.tgz#e3bb35f53cb84ff836cf42fc576b6197c92a5e8a" + integrity sha512-2zVBnOVivWGpl51qO/lycfw7euM4b04AXYUmhsWkUN3FygIwyNgjuiMU8rxQOlu9VGX8X+WXkX2gfbgTovTeFw== + turbo-linux-mips64le@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-linux-mips64le/-/turbo-linux-mips64le-1.4.3.tgz#d796261795e4fc29935cbffdadfbc11d83cbe616" integrity sha512-Ztr1BM5NiUsHWjB7zpkP2RpRDA/fjbLaCbkyfyGlLmVkrSkh05NFBD03IWs2LSLy/wb6vRpL3MQ4FKcb97Tn8w== +turbo-linux-ppc64le@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-linux-ppc64le/-/turbo-linux-ppc64le-1.2.9.tgz#130041711579a1b6b3fe774d85c394425d45c0ac" + integrity sha512-EGgKyzf8IhodOF32BvE3Zlgbr/dSGuUbemC9RGSuhF1F1PMnP1nYS/t3JWN5QKZU4O2uWiIyLdC/0ZjtcGAcZQ== + turbo-linux-ppc64le@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-linux-ppc64le/-/turbo-linux-ppc64le-1.4.3.tgz#b7d043efa27290e074b7aba45bb298fc86bd8cfe" integrity sha512-tJaFJWxwfy/iLd69VHZj6JcXy9hO8LQ+ZUOna/p/wiy5WrFVgEYlD+4gfECfRZ+52EIelMgXl97vACaN1WMhLw== +turbo-windows-32@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-windows-32/-/turbo-windows-32-1.2.9.tgz#89a0d8463dffba01627d69998bfe6ea5fa975511" + integrity sha512-XrMJMUtewlfksBUB0R7Tyw16IoqshVl6f/3R2ccMccddEMcvak0oW03FK9n+Y4F+wyIoJ22AVhu8jMv+HgEehA== + turbo-windows-32@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-windows-32/-/turbo-windows-32-1.4.3.tgz#3dac4678f1d74ccf0ac187e738d30a557932fbad" integrity sha512-w9LyYd+DW3PYFXu9vQiie5lfdqmVIKLV0h181C49hempkIXfgQAosXfaugYWDwBc0GEBoBIQB0vGQKE7gt5nzA== +turbo-windows-64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.2.9.tgz#a55e3f32996ce4a8a538f9667748e7a1129ed10e" + integrity sha512-ewhj4MrqcMpW/keag4xG7YRLTJ7PzcqBc6Kc96OGD2qfK/uJV/r7H3Xt09WuYHRWwPgGEeNn8utpqdqbYfCVDw== + turbo-windows-64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.4.3.tgz#b493ab62db70c4c147cf0dc56dc812ea2e15cf3c" integrity sha512-qPCqemxxOrXyqqig3fVQozRkOwo5oJSsQ3FTZE5YlNu2NwwWvY1mC0X4WTZIDsbj4oHqr0riqC7RGKbjQm1IIQ== +turbo-windows-arm64@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.2.9.tgz#3dc635c0031a0998be0322f9a258553f7cb4eb6e" + integrity sha512-B8BoNb/yZWAyKwQUbs2+UFzLmOu/WGv/+ADT6SQfI8jOaTenS7Od4bbMsGJT0iXcqv+v8TcWKX83KmQ6gxBQpg== + turbo-windows-arm64@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.4.3.tgz#be4b38994cb3f1ceb36940ff6d6b227fa2ba53bf" integrity sha512-djnOOBjw33AnUx2SR6TMOpDr3nKLnVD+HcZvnQz70HyE331AKWjBoEE4rtUOteLAfViWAp3afbiljFSOnbU00Q== +turbo@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.2.9.tgz#61257149a2a29966c9941a16e0b5ad88b07b4e79" + integrity sha512-aPGzZqmUHE9yx9TS7wcAJnDmXiuQSNXDwU5b1KrgNlFuID18TL443wna79p7k4awmf4Yuhu1cSZIvO+se72iVQ== + optionalDependencies: + turbo-darwin-64 "1.2.9" + turbo-darwin-arm64 "1.2.9" + turbo-freebsd-64 "1.2.9" + turbo-freebsd-arm64 "1.2.9" + turbo-linux-32 "1.2.9" + turbo-linux-64 "1.2.9" + turbo-linux-arm "1.2.9" + turbo-linux-arm64 "1.2.9" + turbo-linux-mips64le "1.2.9" + turbo-linux-ppc64le "1.2.9" + turbo-windows-32 "1.2.9" + turbo-windows-64 "1.2.9" + turbo-windows-arm64 "1.2.9" + turbo@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.4.3.tgz#7221972f47a28bfcb53609e97db9810d2c3a265b" @@ -22987,11 +22934,6 @@ typeorm@0.2.41: yargs "^17.0.1" zen-observable-ts "^1.0.0" -typescript@^4.6.4: - version "4.8.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790" - integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw== - typescript@^4.7.4: version "4.7.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" @@ -24667,16 +24609,16 @@ zod-prisma@^0.5.4: parenthesis "^3.1.8" ts-morph "^13.0.2" -zod@^3.16.0, zod@^3.18.0: - version "3.18.0" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.18.0.tgz#2eed58b3cafb8d9a67aa2fee69279702f584f3bc" - integrity sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA== - zod@^3.17.3: version "3.17.3" resolved "https://registry.yarnpkg.com/zod/-/zod-3.17.3.tgz#86abbc670ff0063a4588d85a4dcc917d6e4af2ba" integrity sha512-4oKP5zvG6GGbMlqBkI5FESOAweldEhSOZ6LI6cG+JzUT7ofj1ZOC0PJudpQOpT1iqOFpYYtX5Pw0+o403y4bcg== +zod@^3.18.0: + version "3.18.0" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.18.0.tgz#2eed58b3cafb8d9a67aa2fee69279702f584f3bc" + integrity sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"