diff --git a/.env.example b/.env.example index 3801268..47e8a6a 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,15 @@ +# RPC urls NEXT_PUBLIC_MAINNET= NEXT_PUBLIC_BASE_SEPOLIA= -NEXT_PUBLIC_TESTNETS_ENABLED= +# Enabling this variables adds Base Sepolia in supported markets list +NEXT_PUBLIC_TESTNETS_ENABLED=true -NEXT_PUBLIC_TENDERLY_VNETS_ENABLED= +# If you have forked mainnet in Tenderly provide appropriate values +NEXT_PUBLIC_TENDERLY_VNETS_ENABLED=false NEXT_PUBLIC_TENDERLY_VNET_ID= NEXT_PUBLIC_TENDERLY_VNET_RPC= -NEXT_PUBLIC_TENDERLY_VNET_EXPLORER= \ No newline at end of file + +# Only for e2e tests +NEXT_PUBLIC_E2E_TEST_ENABLED=false +NEXT_PUBLIC_E2E_WALLET_ADDRESS= \ No newline at end of file diff --git a/.gitignore b/.gitignore index c24a835..0f8e217 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ yarn-error.log* # local env files # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables .env +.env.test .env*.local # vercel @@ -43,4 +44,11 @@ yarn-error.log* *.tsbuildinfo # idea files -.idea \ No newline at end of file +.idea + +# Playwright +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/example.spec.ts b/e2e/example.spec.ts new file mode 100644 index 0000000..74b653b --- /dev/null +++ b/e2e/example.spec.ts @@ -0,0 +1,27 @@ +import { expect, test } from "@playwright/test"; +import { Address } from "viem"; +import { chooseForkedChain, connectToMockWallet } from "./helpers"; +import { setERC20Balance } from "./tenderly/setERC20Balance"; + +test.describe("example", () => { + const wallet = process.env.NEXT_PUBLIC_E2E_WALLET_ADDRESS! as Address; + const USDT = "0xdac17f958d2ee523a2206206994597c13d831ec7"; + + test.beforeEach(async () => { + await setERC20Balance({ + wallet, + amount: 1_000_000n, + token: USDT, + }); + }); + + test("example test", async ({ page }) => { + await page.goto("/"); + + await chooseForkedChain(page); + + await connectToMockWallet(page); + + await expect(page.getByRole("link", { name: "Stake" })).toBeVisible(); + }); +}); diff --git a/e2e/helpers.ts b/e2e/helpers.ts new file mode 100644 index 0000000..99ca082 --- /dev/null +++ b/e2e/helpers.ts @@ -0,0 +1,11 @@ +import { Page } from "@playwright/test"; + +export async function chooseForkedChain(page: Page) { + await page.getByRole("combobox").click(); + await page.getByText("Virtual Mainnet").click(); +} + +export async function connectToMockWallet(page: Page) { + await page.getByRole("button", { name: "Connect Wallet" }).click(); + await page.getByTestId("rk-wallet-option-mock").click(); +} diff --git a/e2e/tenderly/client.ts b/e2e/tenderly/client.ts new file mode 100644 index 0000000..67fc98c --- /dev/null +++ b/e2e/tenderly/client.ts @@ -0,0 +1,8 @@ +import { mainnetFork } from "@/configs/mainnetFork"; +import { RPCS } from "@/constants/rpcs"; +import { createPublicClient, http } from "viem"; + +export const client = createPublicClient({ + chain: mainnetFork, + transport: http(RPCS[mainnetFork.id]), +}); diff --git a/e2e/tenderly/setERC20Balance.ts b/e2e/tenderly/setERC20Balance.ts new file mode 100644 index 0000000..b1a733a --- /dev/null +++ b/e2e/tenderly/setERC20Balance.ts @@ -0,0 +1,26 @@ +import { Address, Hex } from "viem"; +import { client } from "./client"; + +export const setERC20Balance = async ({ + wallet, + amount, + token, +}: { + wallet: Address; + amount: bigint; + token: Address; +}) => { + try { + await client.request<{ + method: "tenderly_setBalance"; + Parameters: [erc20: Hex, to: Hex, value: Hex]; + ReturnType: Hex; + }>({ + method: "tenderly_setErc20Balance", + params: [token, wallet, `0x${amount.toString(16)}`], + }); + } catch (error) { + console.error(error); + throw new Error("Failed to set balance of ERC20 token"); + } +}; diff --git a/package-lock.json b/package-lock.json index 998da7a..96f4f43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", + "@playwright/test": "^1.52.0", "@svgr/webpack": "^8.1.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", @@ -59,6 +60,7 @@ "@vitejs/plugin-react": "^4.4.1", "@vitest/coverage-v8": "^3.1.4", "@vitest/ui": "^3.1.4", + "dotenv": "^16.5.0", "eslint": "^9.27.0", "eslint-config-next": "15.3.2", "husky": "^9.1.7", @@ -4091,6 +4093,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -9292,6 +9310,19 @@ "tslib": "^2.0.3" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -13597,6 +13628,53 @@ "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==", "license": "MIT" }, + "node_modules/playwright": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", diff --git a/package.json b/package.json index 4399ba1..7e180c8 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "umbrella-ui", "version": "0.1.0", "private": true, + "type": "module", "scripts": { "dev": "next dev --turbopack", "build": "next build", @@ -10,8 +11,10 @@ "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest run --coverage", - "prepare": "husky", - "test:ci": "vitest related --run --passWithNoTests" + "test:ci": "vitest related --run --passWithNoTests", + "test:e2e": "npx playwright test", + "test:e2e:ui": "npx playwright test --ui", + "prepare": "husky" }, "lint-staged": { "**/*.{ts,tsx,js,jsx}": [ @@ -63,6 +66,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", + "@playwright/test": "^1.52.0", "@svgr/webpack": "^8.1.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", @@ -75,6 +79,7 @@ "@vitejs/plugin-react": "^4.4.1", "@vitest/coverage-v8": "^3.1.4", "@vitest/ui": "^3.1.4", + "dotenv": "^16.5.0", "eslint": "^9.27.0", "eslint-config-next": "15.3.2", "husky": "^9.1.7", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..e24abf9 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,33 @@ +import { defineConfig, devices } from "@playwright/test"; +import dotenv from "dotenv"; +import path from "path"; + +dotenv.config({ path: path.resolve(process.cwd(), ".env.test") }); + +export default defineConfig({ + testDir: "./e2e", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: "html", + + use: { + trace: "on-first-retry", + video: "off", + baseURL: "http://localhost:3000", + }, + + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], + + webServer: { + command: "NODE_ENV=test && npm run build && npm run start", + url: "http://localhost:3000", + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/src/app/web-app-manifest-192x192.png b/public/web-app-manifest-192x192.png similarity index 100% rename from src/app/web-app-manifest-192x192.png rename to public/web-app-manifest-192x192.png diff --git a/src/app/web-app-manifest-512x512.png b/public/web-app-manifest-512x512.png similarity index 100% rename from src/app/web-app-manifest-512x512.png rename to public/web-app-manifest-512x512.png diff --git a/src/app/claim-all-rewards/page.tsx b/src/app/claim-all-rewards/page.tsx index f051e2c..f10add6 100644 --- a/src/app/claim-all-rewards/page.tsx +++ b/src/app/claim-all-rewards/page.tsx @@ -13,7 +13,6 @@ import { useIsSafeWallet } from "@/hooks/useIsSafeWallet/useIsSafeWallet"; import { useWalletAddress } from "@/providers/WalletProvider/WalletContext"; import { sumUpAllRewards } from "@/utils/calculations"; import { withPositiveBalance } from "@/utils/data"; -import { CoinsIcon } from "lucide-react"; export default function ClaimRewardsPage() { const receiver = useWalletAddress(); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 96e361c..27d0433 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -29,7 +29,9 @@ export default function RootLayout({ children }: Readonly) { return ( -