diff --git a/.gitignore b/.gitignore index cb9bb4e57..010a1c23a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,12 @@ types # Vitest coverage +### Playwright + +test-results +playwright-report +playwright/.cache + ### Synpress .cache-synpress diff --git a/biome.json b/biome.json index 77e873c90..e6d3a4f38 100644 --- a/biome.json +++ b/biome.json @@ -8,6 +8,8 @@ "**/dist", "**/types", "**/coverage", + "**/test-results", + "**/playwright-report", "**/.cache-synpress" ] }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1a8d6ab1..bb11e1acd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,12 @@ importers: specifier: workspace:* version: link:../../packages/core devDependencies: + '@playwright/test': + specifier: ^1.38.1 + version: 1.38.1 + '@types/node': + specifier: ^20.8.0 + version: 20.8.0 '@vitest/coverage-v8': specifier: 1.0.0-beta.0 version: 1.0.0-beta.0(vitest@0.34.6) @@ -658,6 +664,14 @@ packages: playwright: 1.38.0 dev: false + /@playwright/test@1.38.1: + resolution: {integrity: sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==} + engines: {node: '>=16'} + hasBin: true + dependencies: + playwright: 1.38.1 + dev: true + /@scure/base@1.1.3: resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} dev: false @@ -736,7 +750,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.0 dev: false /@types/ms@0.7.32: @@ -747,13 +761,8 @@ packages: resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} dev: false - /@types/node@20.6.3: - resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==} - dev: false - /@types/node@20.8.0: resolution: {integrity: sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==} - dev: true /@types/readdir-glob@1.1.2: resolution: {integrity: sha512-vwAYrNN/8yhp/FJRU6HUSD0yk6xfoOS8HrZa8ZL7j+X8hJpaC1hTcAiXX2IxaAkkvrz9mLyoEhYZTE3cEYvA9Q==} @@ -764,7 +773,7 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.0 dev: false /@types/set-cookie-parser@2.4.4: @@ -782,7 +791,7 @@ packages: /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.0 dev: false /@vitest/coverage-v8@1.0.0-beta.0(vitest@0.34.6): @@ -1791,7 +1800,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: false optional: true /fsevents@2.3.3: @@ -2875,6 +2883,12 @@ packages: hasBin: true dev: false + /playwright-core@1.38.1: + resolution: {integrity: sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==} + engines: {node: '>=16'} + hasBin: true + dev: true + /playwright@1.38.0: resolution: {integrity: sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==} engines: {node: '>=16'} @@ -2885,6 +2899,16 @@ packages: fsevents: 2.3.2 dev: false + /playwright@1.38.1: + resolution: {integrity: sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==} + engines: {node: '>=16'} + hasBin: true + dependencies: + playwright-core: 1.38.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /postcss-load-config@4.0.1: resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} diff --git a/wallets/metamask/environment.d.ts b/wallets/metamask/environment.d.ts new file mode 100644 index 000000000..e214b5b09 --- /dev/null +++ b/wallets/metamask/environment.d.ts @@ -0,0 +1,10 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + CI: boolean + HEADLESS: boolean + } + } +} + +export {} diff --git a/wallets/metamask/package.json b/wallets/metamask/package.json index 435476556..248553213 100644 --- a/wallets/metamask/package.json +++ b/wallets/metamask/package.json @@ -23,6 +23,8 @@ "clean": "rimraf dist types", "test": "vitest run", "test:coverage": "vitest run --coverage", + "test:e2e": "playwright test", + "test:e2e:headless": "HEADLESS=true playwright test", "test:watch": "vitest watch", "types:check": "tsc --noEmit" }, @@ -30,6 +32,8 @@ "core": "workspace:*" }, "devDependencies": { + "@playwright/test": "^1.38.1", + "@types/node": "^20.8.0", "@vitest/coverage-v8": "1.0.0-beta.0", "rimraf": "^5.0.1", "tsconfig": "workspace:*", diff --git a/wallets/metamask/playwright.config.ts b/wallets/metamask/playwright.config.ts new file mode 100644 index 000000000..b2d851aef --- /dev/null +++ b/wallets/metamask/playwright.config.ts @@ -0,0 +1,36 @@ +import { defineConfig, devices } from '@playwright/test' + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + // Look for test files in the "tests/e2e" directory, relative to this configuration file. + testDir: './test/e2e', + + // Run all tests in parallel. + // TODO: Enable later once we have more tests. + fullyParallel: false, + + // Fail the build on CI if you accidentally left test.only in the source code. + forbidOnly: !!process.env.CI, + + // Opt out of parallel tests on CI. + workers: process.env.CI ? 1 : undefined, + + // Concise 'dot' for CI, default 'html' when running locally. See https://playwright.dev/docs/test-reporters. + reporter: process.env.CI ? 'dot' : 'html', + + // Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. + use: { + // Collect traces for all failed tests. See https://playwright.dev/docs/trace-viewer. + trace: 'retain-on-failure' + }, + + // Configure projects for major browsers. + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + } + ] +}) diff --git a/wallets/metamask/test/e2e/prepareExtension.spec.ts b/wallets/metamask/test/e2e/prepareExtension.spec.ts new file mode 100644 index 000000000..9adfad1c8 --- /dev/null +++ b/wallets/metamask/test/e2e/prepareExtension.spec.ts @@ -0,0 +1,45 @@ +import { type Page, chromium, test as base } from '@playwright/test' +import { prepareExtension } from '../../src/prepareExtension' + +const test = base.extend({ + context: async ({ context: _ }, use) => { + const metamaskPath = await prepareExtension() + + const browserArgs = [ + `--disable-extensions-except=${metamaskPath}`, + `--load-extension=${metamaskPath}` + ] + + if (process.env.HEADLESS) { + browserArgs.push('--headless=new') + } + + const context = await chromium.launchPersistentContext('', { + headless: false, + args: browserArgs + }) + + try { + await context.waitForEvent('page', { timeout: 5000 }) + } catch { + throw new Error('[FIXTURE] MetaMask extension did not load in time') + } + + await use(context) + }, + page: async ({ context }, use) => { + const metamaskOnboardingPage = context.pages()[1] as Page + await use(metamaskOnboardingPage) + } +}) + +const { describe, expect } = test + +describe('prepareExtension', () => { + test('onboarding page opens up', async ({ page }) => { + await expect(page).toHaveTitle('MetaMask') + await expect( + page.getByRole('heading', { name: "Let's get started" }) + ).toBeVisible() + }) +}) diff --git a/wallets/metamask/test/prepareExtension.test.ts b/wallets/metamask/test/unit/prepareExtension.test.ts similarity index 98% rename from wallets/metamask/test/prepareExtension.test.ts rename to wallets/metamask/test/unit/prepareExtension.test.ts index 584f2d1e9..87b5c4bc4 100644 --- a/wallets/metamask/test/prepareExtension.test.ts +++ b/wallets/metamask/test/unit/prepareExtension.test.ts @@ -4,7 +4,7 @@ import { DEFAULT_METAMASK_VERSION, EXTENSION_DOWNLOAD_URL, prepareExtension -} from '../src/prepareExtension' +} from '../../src/prepareExtension' const MOCK_CACHE_DIR_PATH = 'mockCacheDirPath' const MOCK_EXTENSION_ARCHIVE_PATH = 'mockExtensionArchivePath' diff --git a/wallets/metamask/tsconfig.json b/wallets/metamask/tsconfig.json index f210b2a2f..d5e9e89c1 100644 --- a/wallets/metamask/tsconfig.json +++ b/wallets/metamask/tsconfig.json @@ -1,10 +1,16 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "rootDir": "." + "rootDir": ".", + "exactOptionalPropertyTypes": false // Allows for `undefined` in `playwright.config.ts` }, "include": [ "src", "test" + ], + "files": [ + "environment.d.ts", + "playwright.config.ts", + "vitest.config.ts", ] } diff --git a/wallets/metamask/vitest.config.ts b/wallets/metamask/vitest.config.ts new file mode 100644 index 000000000..42ec65bf9 --- /dev/null +++ b/wallets/metamask/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + dir: 'test/unit' + } +})