diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 000000000..10d2ec42c --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,27 @@ +# name: End-to-End tests +# on: +# push: +# branches: [main] +# pull_request: +# branches: [main] +# jobs: +# test: +# timeout-minutes: 60 +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: actions/setup-node@v4 +# with: +# node-version: lts/* +# - name: Install dependencies +# run: npm install -g pnpm && pnpm install +# - name: Install Playwright Browsers +# run: pnpm exec playwright install --with-deps +# - name: Run Playwright tests +# run: pnpm exec playwright test +# - uses: actions/upload-artifact@v4 +# if: always() +# with: +# name: playwright-report +# path: playwright-report/ +# retention-days: 30 diff --git a/examples/starknet-react-next/.gitignore b/examples/starknet-react-next/.gitignore new file mode 100644 index 000000000..68c5d18f0 --- /dev/null +++ b/examples/starknet-react-next/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/examples/starknet-react-next/package.json b/examples/starknet-react-next/package.json index d2c7a2899..66c099b8e 100644 --- a/examples/starknet-react-next/package.json +++ b/examples/starknet-react-next/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "next dev -p 3002", "build": "next build", + "test": "playwright test", "start": "next start -p 3002", "lint": "next lint", "format": "prettier --write ." @@ -23,6 +24,7 @@ }, "devDependencies": { "@cartridge/tsconfig": "workspace:^", + "@playwright/test": "^1.44.1", "@types/node": "^20.6.0", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", diff --git a/examples/starknet-react-next/playwright.config.ts b/examples/starknet-react-next/playwright.config.ts new file mode 100644 index 000000000..652644092 --- /dev/null +++ b/examples/starknet-react-next/playwright.config.ts @@ -0,0 +1,33 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./tests", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: "html", + use: { + baseURL: "http://localhost:3002", + trace: "on-first-retry", + }, + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + // { + // name: "firefox", + // use: { ...devices["Desktop Firefox"] }, + // }, + // { + // name: "webkit", + // use: { ...devices["Desktop Safari"] }, + // }, + ], + webServer: { + command: "cd ../../ && pnpm dev", + url: "http://localhost:3002", + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/examples/starknet-react-next/tests/example.spec.ts b/examples/starknet-react-next/tests/example.spec.ts new file mode 100644 index 000000000..e6a937ceb --- /dev/null +++ b/examples/starknet-react-next/tests/example.spec.ts @@ -0,0 +1,44 @@ +import { test, expect, CDPSession } from "@playwright/test"; + +let client: CDPSession; +let authenticatorId: string; + +test.beforeEach(async ({ page }) => { + client = await page.context().newCDPSession(page); + await client.send("WebAuthn.enable"); + const result = await client.send("WebAuthn.addVirtualAuthenticator", { + options: { + protocol: "ctap2", + transport: "internal", + hasResidentKey: true, + hasUserVerification: true, + isUserVerified: true, + automaticPresenceSimulation: true, + }, + }); + authenticatorId = result.authenticatorId; + + await page.goto("/"); +}); + +test("has title", async ({ page }) => { + await expect(page).toHaveTitle(/StarkNet ❤️ React/); +}); + +test.describe("Connect", () => { + test.describe("Log in", () => { + test("should allow me to login and connect to Controller", async ({ + page, + }) => { + await page.getByRole("button", { name: "Connect" }).click(); + const modal = page.frameLocator("#cartridge-modal"); + await expect( + modal.getByText("Play with Cartridge Controller"), + ).toBeVisible(); + await modal.getByPlaceholder("Username").fill("test-0"); + await modal.getByRole("button", { name: "LOG IN" }).click(); + + await expect(page.getByText("Address: ")).toBeVisible(); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 456e2cfaf..dcb1c9a52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,7 +68,7 @@ importers: version: 2.7.0(get-starknet-core@4.0.0)(react@18.3.1)(starknet@6.11.0) next: specifier: ^14.2.5 - version: 14.2.5(@babel/core@7.24.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.5(@babel/core@7.24.5)(@playwright/test@1.44.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -88,6 +88,9 @@ importers: '@cartridge/tsconfig': specifier: workspace:^ version: link:../../packages/tsconfig + '@playwright/test': + specifier: ^1.44.1 + version: 1.44.1 '@types/node': specifier: ^20.6.0 version: 20.12.7 @@ -381,7 +384,7 @@ importers: version: 7.6.18(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/nextjs': specifier: ^7.4.2 - version: 7.6.18(@next/font@13.5.6)(@swc/core@1.4.17(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)(esbuild@0.18.20)(next@14.2.5(@babel/core@7.24.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.4.5)(webpack-dev-server@4.15.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)) + version: 7.6.18(@next/font@13.5.6)(@swc/core@1.4.17(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)(esbuild@0.18.20)(next@14.2.5(@babel/core@7.24.5)(@playwright/test@1.44.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.4.5)(webpack-dev-server@4.15.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)) '@storybook/react': specifier: ^7.1.1 version: 7.6.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) @@ -3357,6 +3360,11 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@playwright/test@1.44.1': + resolution: {integrity: sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==} + engines: {node: '>=16'} + hasBin: true + '@pmmmwh/react-refresh-webpack-plugin@0.5.13': resolution: {integrity: sha512-odZVYXly+JwzYri9rKqqUAk0cY6zLpv4dxoKinhoJNShV36Gpxf+CyDIILJ4tYsJ1ZxIWs233Y39iVnynvDA/g==} engines: {node: '>= 10.13'} @@ -7857,6 +7865,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -10104,6 +10117,16 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} + playwright-core@1.44.1: + resolution: {integrity: sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==} + engines: {node: '>=16'} + hasBin: true + + playwright@1.44.1: + resolution: {integrity: sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==} + engines: {node: '>=16'} + hasBin: true + plimit-lit@1.6.1: resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} engines: {node: '>=12'} @@ -16921,6 +16944,10 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@playwright/test@1.44.1': + dependencies: + playwright: 1.44.1 + '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@2.19.0)(webpack-dev-server@4.15.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20))': dependencies: ansi-html-community: 0.0.8 @@ -18962,7 +18989,7 @@ snapshots: '@storybook/mdx2-csf@1.1.0': {} - '@storybook/nextjs@7.6.18(@next/font@13.5.6)(@swc/core@1.4.17(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)(esbuild@0.18.20)(next@14.2.5(@babel/core@7.24.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.4.5)(webpack-dev-server@4.15.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20))': + '@storybook/nextjs@7.6.18(@next/font@13.5.6)(@swc/core@1.4.17(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)(esbuild@0.18.20)(next@14.2.5(@babel/core@7.24.5)(@playwright/test@1.44.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.4.5)(webpack-dev-server@4.15.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20))': dependencies: '@babel/core': 7.24.5 '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.5) @@ -18992,7 +19019,7 @@ snapshots: fs-extra: 11.2.0 image-size: 1.1.1 loader-utils: 3.2.1 - next: 14.2.5(@babel/core@7.24.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.5(@babel/core@7.24.5)(@playwright/test@1.44.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) node-polyfill-webpack-plugin: 2.0.1(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.18.20)) pnp-webpack-plugin: 1.7.0(typescript@5.4.5) postcss: 8.4.38 @@ -23683,6 +23710,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -25744,7 +25774,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@14.2.5(@babel/core@7.24.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.5(@babel/core@7.24.5)(@playwright/test@1.44.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.5 '@swc/helpers': 0.5.5 @@ -25765,6 +25795,7 @@ snapshots: '@next/swc-win32-arm64-msvc': 14.2.5 '@next/swc-win32-ia32-msvc': 14.2.5 '@next/swc-win32-x64-msvc': 14.2.5 + '@playwright/test': 1.44.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -26254,6 +26285,14 @@ snapshots: dependencies: find-up: 3.0.0 + playwright-core@1.44.1: {} + + playwright@1.44.1: + dependencies: + playwright-core: 1.44.1 + optionalDependencies: + fsevents: 2.3.2 + plimit-lit@1.6.1: dependencies: queue-lit: 1.5.2