Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ jobs:
- run:
name: Update ENV for E2E test
command: |
echo 'export VITE_RC_API_KEY=${E2E_RC_API_KEY}; export VITE_RC_TAX_E2E_API_KEY=${E2E_RC_TAX_E2E_API_KEY}; export VITE_RC_STRIPE_CHECKOUT_E2E_API_KEY=${E2E_RC_STRIPE_CHECKOUT_E2E_API_KEY}; export VITE_SKIP_TAX_REAL_TESTS_UNTIL=${VITE_SKIP_TAX_REAL_TESTS_UNTIL}; export VITE_SKIP_STRIPE_TESTS=${VITE_SKIP_STRIPE_TESTS:-false}; export VITE_ALLOW_PAYWALLS_TESTS=true' >> "$BASH_ENV"
echo 'export VITE_RC_API_KEY=${E2E_RC_API_KEY}; export VITE_RC_TAX_E2E_API_KEY=${E2E_RC_TAX_E2E_API_KEY}; export VITE_RC_STRIPE_CHECKOUT_E2E_API_KEY=${E2E_RC_STRIPE_CHECKOUT_E2E_API_KEY}; export VITE_RC_PADDLE_E2E_API_KEY=${E2E_RC_PADDLE_E2E_API_KEY}; export VITE_SKIP_TAX_REAL_TESTS_UNTIL=${VITE_SKIP_TAX_REAL_TESTS_UNTIL}; export VITE_SKIP_STRIPE_TESTS=${VITE_SKIP_STRIPE_TESTS:-false}; export VITE_SKIP_PADDLE_TESTS=${VITE_SKIP_PADDLE_TESTS:-false}; export VITE_ALLOW_PAYWALLS_TESTS=true' >> "$BASH_ENV"
source "$BASH_ENV"
- install-dependencies
- run:
Expand Down
1 change: 1 addition & 0 deletions examples/webbilling-demo/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ VITE_RC_API_KEY="rcb_abcdef1234567890abcdef"
VITE_RC_NON_TAX_E2E_API_KEY="rcb_abcdef1234567890abcdef"
VITE_RC_TAX_E2E_API_KEY="rcb_abcdef1234567890abcdef"
VITE_RC_STRIPE_CHECKOUT_E2E_API_KEY="strp_abcdef1234567890abcdef"
VITE_RC_PADDLE_E2E_API_KEY="pdl_abcdef1234567890abcdef"
VITE_SKIP_STRIPE_TESTS=false
VITE_SKIP_PADDLE_TESTS=false
VITE_SKIP_TAX_REAL_TESTS_UNTIL=2199-10-18 # Do not run in local environment by default
Expand Down
4 changes: 4 additions & 0 deletions examples/webbilling-demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,17 @@ The SDK automatically detects Paddle API keys and routes to the Paddle flow. The
export VITE_RC_NON_TAX_E2E_API_KEY = 'your e2e tests public api key'
export VITE_RC_TAX_E2E_API_KEY = 'your e2e tests public api key'
export VITE_RC_STRIPE_CHECKOUT_E2E_API_KEY = 'your stripe checkout e2e tests public api key'
export VITE_RC_PADDLE_E2E_API_KEY = 'your paddle e2e tests public api key'
```

Optional flags:

```bash
# Useful if Stripe rate limiting is causing flaky CI runs.
export VITE_SKIP_STRIPE_TESTS=true

# Useful to temporarily disable Paddle tests.
export VITE_SKIP_PADDLE_TESTS=true
```

Install playwright
Expand Down
1 change: 1 addition & 0 deletions examples/webbilling-demo/src/tests/helpers/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const NON_TAX_TEST_API_KEY = process.env.VITE_RC_NON_TAX_E2E_API_KEY;
export const TAX_TEST_API_KEY = process.env.VITE_RC_TAX_E2E_API_KEY;
export const STRIPE_CHECKOUT_TEST_API_KEY =
process.env.VITE_RC_STRIPE_CHECKOUT_E2E_API_KEY;
export const PADDLE_TEST_API_KEY = process.env.VITE_RC_PADDLE_E2E_API_KEY;
export const TAX_TEST_OFFERING_ID = "rcb_e2e_taxes";
export const TAX_TEST_OFFERING_ID_WITH_DISCOUNT = "rcb_e2e_taxes_discounted";
export const TAX_TEST_DISCOUNT_CODE = "FOREVER10";
Expand Down
32 changes: 24 additions & 8 deletions examples/webbilling-demo/src/tests/helpers/integration-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export const SKIP_STRIPE_TESTS =
process.env.VITE_SKIP_STRIPE_TESTS === "true" ||
process.env.VITE_SKIP_STRIPE_TESTS === "1";

export const SKIP_PADDLE_TESTS =
process.env.VITE_SKIP_PADDLE_TESTS === "true" ||
process.env.VITE_SKIP_PADDLE_TESTS === "1";

export const SKIP_TAX_REAL_TESTS = (() => {
const skipUntilDate = process.env.VITE_SKIP_TAX_REAL_TESTS_UNTIL;
if (!skipUntilDate) return false;
Expand Down Expand Up @@ -55,14 +59,20 @@ interface TestFixtures {
}

export const integrationTest = test.extend<TestFixtures>({
userId: async ({ browserName }, use) => {
const userId = getUserId(browserName);
await use(userId);
},
email: async ({ userId }, use) => {
const email = getEmailFromUserId(userId);
await use(email);
},
userId: [
async ({ browserName }, use) => {
const userId = getUserId(browserName);
await use(userId);
},
{ scope: "test" },
],
email: [
async ({ userId }, use) => {
const email = getEmailFromUserId(userId);
await use(email);
},
{ scope: "test" },
],
page: async ({ browser }, use) => {
const page = await browser.newPage();
await use(page);
Expand All @@ -72,6 +82,12 @@ export const integrationTest = test.extend<TestFixtures>({

integrationTest.beforeEach(async ({ page }) => {
await page.route("**/v1/events", async (route) => {
const url = route.request().url();
if (!url.includes("revenuecat.com/v1/events")) {
await route.continue();
return;
}

await route.fulfill({
status: 200,
body: JSON.stringify({}),
Expand Down
13 changes: 12 additions & 1 deletion examples/webbilling-demo/src/tests/helpers/test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { type Page, type Locator, expect } from "@playwright/test";
import type { StoreLoadTime } from "@revenuecat/purchases-js";
import { BASE_URL, NON_TAX_TEST_API_KEY } from "./fixtures";
import type { integrationTest } from "./integration-test";
import { ALLOW_PAYWALLS_TESTS, SKIP_STRIPE_TESTS } from "./integration-test";
import {
ALLOW_PAYWALLS_TESTS,
SKIP_PADDLE_TESTS,
SKIP_STRIPE_TESTS,
} from "./integration-test";

export type RouteFulfillOptions = {
body?: string | Buffer | undefined;
Expand Down Expand Up @@ -37,6 +41,13 @@ export const skipStripeTestsIfDisabled = (test: typeof integrationTest) => {
);
};

export const skipPaddleTestsIfDisabled = (test: typeof integrationTest) => {
test.skip(
SKIP_PADDLE_TESTS,
"Paddle tests are disabled. To enable them, unset VITE_SKIP_PADDLE_TESTS or set it to false.",
);
};

function getRandomHash(length: number = 6): string {
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
return Array.from({ length }, () =>
Expand Down
152 changes: 152 additions & 0 deletions examples/webbilling-demo/src/tests/paddle/purchase-flow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { expect } from "@playwright/test";
import { PADDLE_TEST_API_KEY } from "../helpers/fixtures";
import { integrationTest } from "../helpers/integration-test";
import {
confirmPaymentError,
getPackageCards,
getPaywallPackageCards,
getPaywallPurchaseButtons,
skipPaddleTestsIfDisabled,
skipPaywallsTestIfDisabled,
startPurchaseFlow,
} from "../helpers/test-helpers";
import {
completePaddleCheckoutForm,
confirmPaddleProcessingPayment,
navigateToPaddleLandingUrl,
PADDLE_TEST_TIMEOUT_MS,
PADDLE_UI_STEP_TIMEOUT_MS,
} from "./test-helpers";

integrationTest.describe("Paddle flow", () => {
const paddleCardholderName = "RevenueCat E2E";

integrationTest.describe.configure({
timeout: PADDLE_TEST_TIMEOUT_MS,
});

integrationTest.skip(
({ browserName }) => !!process.env.CI && browserName !== "chromium",
"Paddle tests run only in Chromium on CI",
);

skipPaddleTestsIfDisabled(integrationTest);

integrationTest.skip(
!PADDLE_TEST_API_KEY,
"Paddle E2E tests require VITE_RC_PADDLE_E2E_API_KEY.",
);

integrationTest(
"Purchases a product with Paddle",
async ({ page, userId, email }) => {
page = await navigateToPaddleLandingUrl(page, userId);

await expect(page.getByText("Paddle demo")).toBeVisible({
timeout: PADDLE_UI_STEP_TIMEOUT_MS,
});

const packageCards = await getPackageCards(page);
expect(packageCards.length).toBeGreaterThan(0);

await startPurchaseFlow(packageCards[0]);
await completePaddleCheckoutForm(page, email, paddleCardholderName);
await confirmPaddleProcessingPayment(page);
},
);

integrationTest(
"Purchases a product with Paddle passing the email as query parameter",
async ({ page, userId, email }) => {
page = await navigateToPaddleLandingUrl(page, userId, {
email,
});

await expect(page.getByText("Paddle demo")).toBeVisible({
timeout: PADDLE_UI_STEP_TIMEOUT_MS,
});

const packageCards = await getPackageCards(page);
expect(packageCards.length).toBeGreaterThan(0);

await startPurchaseFlow(packageCards[0]);
await completePaddleCheckoutForm(
page,
email,
paddleCardholderName,
false,
);
await confirmPaddleProcessingPayment(page);
},
);

integrationTest(
"Shows an error screen when checkout/start returns missing paddle checkout params",
async ({ page, userId, email }) => {
page = await navigateToPaddleLandingUrl(page, userId, {
email,
});

await expect(page.getByText("Paddle demo")).toBeVisible({
timeout: PADDLE_UI_STEP_TIMEOUT_MS,
});

await page.route("*/**/checkout/start", async (route) => {
const response = await route.fetch();
const json = (await response.json()) as Record<string, unknown>;

await route.fulfill({
response,
json: {
...json,
paddle_billing_params: null,
},
});
});

const packageCards = await getPackageCards(page);
expect(packageCards.length).toBeGreaterThan(0);

await startPurchaseFlow(packageCards[0]);
await confirmPaymentError(
page,
"Something went wrong",
PADDLE_UI_STEP_TIMEOUT_MS,
);
await confirmPaymentError(
page,
/An unknown error occurred/i,
PADDLE_UI_STEP_TIMEOUT_MS,
);
},
);

integrationTest(
"Purchases monthly product from RC Paywall with Paddle",
async ({ page, userId, email }) => {
skipPaywallsTestIfDisabled(integrationTest);

page = await navigateToPaddleLandingUrl(page, userId, {
useRcPaywall: true,
lang: "en",
email,
});

const paywallPackages = await getPaywallPackageCards(page);
expect(paywallPackages.length).toBeGreaterThan(0);
await paywallPackages[0].click();

const purchaseButtons = await getPaywallPurchaseButtons(page);
expect(purchaseButtons.length).toBeGreaterThan(0);
await purchaseButtons[0].click();

await completePaddleCheckoutForm(
page,
email,
paddleCardholderName,
false,
);
await confirmPaddleProcessingPayment(page);
},
);
});
Loading