|
1 | | -import { expect } from "@playwright/test"; |
2 | | -import type { Page } from "@playwright/test"; |
3 | | - |
4 | 1 | import prisma from "@calcom/prisma"; |
5 | | - |
| 2 | +import type { Page } from "@playwright/test"; |
| 3 | +import { expect } from "@playwright/test"; |
6 | 4 | import { test } from "./lib/fixtures"; |
7 | 5 | import { selectFirstAvailableTimeSlotNextMonth, submitAndWaitForResponse } from "./lib/testUtils"; |
8 | 6 |
|
9 | 7 | test.describe.configure({ mode: "parallel" }); |
10 | 8 | test.afterEach(({ users }) => users.deleteAll()); |
11 | 9 |
|
12 | | -async function goToAppsTab(page: Page, eventTypeId?: number) { |
| 10 | +async function goToAppsTab(page: Page, eventTypeId?: number): Promise<void> { |
13 | 11 | await page.goto(`event-types/${eventTypeId}?tabName=apps`); |
14 | 12 | await expect(page.getByTestId("vertical-tab-apps").first()).toHaveAttribute("aria-current", "page"); |
15 | 13 | } |
16 | 14 |
|
| 15 | +/** |
| 16 | + * Ensures an app is enabled in the App table and returns a cleanup function. |
| 17 | + * This is needed because apps without valid keys are now disabled during seeding. |
| 18 | + * For E2E tests, we need to enable the app to test the UI behavior. |
| 19 | + * The cleanup function restores the original enabled state to avoid side-effects. |
| 20 | + */ |
| 21 | +async function ensureAppEnabled(appSlug: string): Promise<() => Promise<void>> { |
| 22 | + const app = await prisma.app.findUnique({ |
| 23 | + where: { slug: appSlug }, |
| 24 | + select: { enabled: true }, |
| 25 | + }); |
| 26 | + const originalEnabled = app?.enabled ?? false; |
| 27 | + |
| 28 | + await prisma.app.update({ |
| 29 | + where: { slug: appSlug }, |
| 30 | + data: { enabled: true }, |
| 31 | + }); |
| 32 | + |
| 33 | + return async () => { |
| 34 | + await prisma.app.update({ |
| 35 | + where: { slug: appSlug }, |
| 36 | + data: { enabled: originalEnabled }, |
| 37 | + }); |
| 38 | + }; |
| 39 | +} |
| 40 | + |
| 41 | +// biome-ignore lint/complexity/noExcessiveLinesPerFunction: E2E test suites naturally have many test cases |
17 | 42 | test.describe("Payment app", () => { |
18 | 43 | test("Should be able to edit alby price, currency", async ({ page, users }) => { |
19 | 44 | const user = await users.create(); |
20 | 45 | await user.apiLogin(); |
21 | 46 | const paymentEvent = user.eventTypes.find((item) => item.slug === "paid"); |
22 | 47 | expect(paymentEvent).not.toBeNull(); |
23 | | - await prisma.credential.create({ |
24 | | - data: { |
25 | | - type: "alby_payment", |
26 | | - appId: "alby", |
27 | | - userId: user.id, |
28 | | - key: { |
29 | | - account_id: "random", |
30 | | - account_email: "random@example.com", |
31 | | - webhook_endpoint_id: "ep_randomString", |
32 | | - webhook_endpoint_secret: "whsec_randomString", |
33 | | - account_lightning_address: "random@getalby.com", |
| 48 | + // Ensure alby app is enabled (it may be disabled if keys are not configured) |
| 49 | + const cleanupAlbyApp = await ensureAppEnabled("alby"); |
| 50 | + try { |
| 51 | + await prisma.credential.create({ |
| 52 | + data: { |
| 53 | + type: "alby_payment", |
| 54 | + appId: "alby", |
| 55 | + userId: user.id, |
| 56 | + key: { |
| 57 | + account_id: "random", |
| 58 | + account_email: "random@example.com", |
| 59 | + webhook_endpoint_id: "ep_randomString", |
| 60 | + webhook_endpoint_secret: "whsec_randomString", |
| 61 | + account_lightning_address: "random@getalby.com", |
| 62 | + }, |
34 | 63 | }, |
35 | | - }, |
36 | | - }); |
| 64 | + }); |
37 | 65 |
|
38 | | - await goToAppsTab(page, paymentEvent?.id); |
| 66 | + await goToAppsTab(page, paymentEvent?.id); |
39 | 67 |
|
40 | | - await page.locator("#event-type-form").getByRole("switch").click(); |
41 | | - await page.getByPlaceholder("Price").click(); |
42 | | - await page.getByPlaceholder("Price").fill("200"); |
43 | | - await page.getByText("SatoshissatsCurrencyBTCPayment optionCollect payment on booking").click(); |
44 | | - await submitAndWaitForResponse(page, "/api/trpc/eventTypesHeavy/update?batch=1", { |
45 | | - action: () => page.locator("[data-testid=update-eventtype]").click(), |
46 | | - }); |
| 68 | + await page.locator("#event-type-form").getByRole("switch").click(); |
| 69 | + await page.getByPlaceholder("Price").click(); |
| 70 | + await page.getByPlaceholder("Price").fill("200"); |
| 71 | + await page.getByText("SatoshissatsCurrencyBTCPayment optionCollect payment on booking").click(); |
| 72 | + await submitAndWaitForResponse(page, "/api/trpc/eventTypesHeavy/update?batch=1", { |
| 73 | + action: () => page.locator("[data-testid=update-eventtype]").click(), |
| 74 | + }); |
47 | 75 |
|
48 | | - await page.goto(`${user.username}/${paymentEvent?.slug}`); |
| 76 | + await page.goto(`${user.username}/${paymentEvent?.slug}`); |
49 | 77 |
|
50 | | - // expect 200 sats to be displayed in page |
51 | | - await expect(page.locator("text=200 sats").first()).toBeVisible(); |
| 78 | + // expect 200 sats to be displayed in page |
| 79 | + await expect(page.locator("text=200 sats").first()).toBeVisible(); |
52 | 80 |
|
53 | | - await selectFirstAvailableTimeSlotNextMonth(page); |
54 | | - await expect(page.locator("text=200 sats").first()).toBeVisible(); |
| 81 | + await selectFirstAvailableTimeSlotNextMonth(page); |
| 82 | + await expect(page.locator("text=200 sats").first()).toBeVisible(); |
55 | 83 |
|
56 | | - // go to /event-types and check if the price is 200 sats |
57 | | - await page.goto(`event-types/`); |
58 | | - await expect(page.locator("text=200 sats").first()).toBeVisible(); |
| 84 | + // go to /event-types and check if the price is 200 sats |
| 85 | + await page.goto(`event-types/`); |
| 86 | + await expect(page.locator("text=200 sats").first()).toBeVisible(); |
| 87 | + } finally { |
| 88 | + await cleanupAlbyApp(); |
| 89 | + } |
59 | 90 | }); |
60 | 91 |
|
61 | 92 | test("Should be able to edit stripe price, currency", async ({ page, users }) => { |
@@ -155,26 +186,32 @@ test.describe("Payment app", () => { |
155 | 186 | await user.apiLogin(); |
156 | 187 | const paymentEvent = user.eventTypes.find((item) => item.slug === "paid"); |
157 | 188 | expect(paymentEvent).not.toBeNull(); |
158 | | - await prisma.credential.create({ |
159 | | - data: { |
160 | | - type: "alby_payment", |
161 | | - appId: "alby", |
162 | | - userId: user.id, |
163 | | - key: {}, |
164 | | - }, |
165 | | - }); |
| 189 | + // Ensure alby app is enabled (it may be disabled if keys are not configured) |
| 190 | + const cleanupAlbyApp = await ensureAppEnabled("alby"); |
| 191 | + try { |
| 192 | + await prisma.credential.create({ |
| 193 | + data: { |
| 194 | + type: "alby_payment", |
| 195 | + appId: "alby", |
| 196 | + userId: user.id, |
| 197 | + key: {}, |
| 198 | + }, |
| 199 | + }); |
166 | 200 |
|
167 | | - await goToAppsTab(page, paymentEvent?.id); |
| 201 | + await goToAppsTab(page, paymentEvent?.id); |
168 | 202 |
|
169 | | - await page.locator("#event-type-form").getByRole("switch").click(); |
| 203 | + await page.locator("#event-type-form").getByRole("switch").click(); |
170 | 204 |
|
171 | | - // expect text "This app has not been setup yet" to be displayed |
172 | | - expect(await page.locator("text=This app has not been setup yet").first()).toBeTruthy(); |
| 205 | + // expect text "This app has not been setup yet" to be displayed |
| 206 | + expect(await page.locator("text=This app has not been setup yet").first()).toBeTruthy(); |
173 | 207 |
|
174 | | - await page.getByRole("button", { name: "Setup" }).click(); |
| 208 | + await page.getByRole("button", { name: "Setup" }).click(); |
175 | 209 |
|
176 | | - // Expect "Connect with Alby" to be displayed |
177 | | - expect(await page.locator("text=Connect with Alby").first()).toBeTruthy(); |
| 210 | + // Expect "Connect with Alby" to be displayed |
| 211 | + expect(await page.locator("text=Connect with Alby").first()).toBeTruthy(); |
| 212 | + } finally { |
| 213 | + await cleanupAlbyApp(); |
| 214 | + } |
178 | 215 | }); |
179 | 216 |
|
180 | 217 | test("Should display App is not setup already for paypal", async ({ page, users }) => { |
|
0 commit comments