diff --git a/.github/workflows/ui_tests.yml b/.github/workflows/ui_tests.yml index e23a7b7bb4..de3d6693c4 100644 --- a/.github/workflows/ui_tests.yml +++ b/.github/workflows/ui_tests.yml @@ -43,6 +43,7 @@ jobs: - name: Run Playwright tests env: NEXT_PUBLIC_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_PROJECT_ID }} + NEXTAUTH_SECRET: ${{ secrets.TESTS_NEXTAUTH_SECRET }} working-directory: ./apps/laboratory/ run: npm run playwright:test - uses: actions/upload-artifact@v3 diff --git a/apps/laboratory/playwright.config.ts b/apps/laboratory/playwright.config.ts index fccd3d052f..6231eb6133 100644 --- a/apps/laboratory/playwright.config.ts +++ b/apps/laboratory/playwright.config.ts @@ -14,7 +14,7 @@ export default defineConfig({ reporter: [['list'], ['html']], expect: { - timeout: (process.env['CI'] ? 60 : 5) * 1000 + timeout: (process.env['CI'] ? 60 : 15) * 1000 }, timeout: 60 * 1000, diff --git a/apps/laboratory/src/constants.ts b/apps/laboratory/src/constants.ts index 3e953fa98a..666456db8c 100644 --- a/apps/laboratory/src/constants.ts +++ b/apps/laboratory/src/constants.ts @@ -1,2 +1,3 @@ export const SigningSucceededToastTitle = 'Signing Succeeded' export const SigningFailedToastTitle = 'Signing Failed' +export const TestIdSiweAuthenticationStatus = 'w3m-authentication-status' diff --git a/apps/laboratory/src/pages/library/ethers-siwe.tsx b/apps/laboratory/src/pages/library/ethers-siwe.tsx index 2caf26c92c..4373ddc636 100644 --- a/apps/laboratory/src/pages/library/ethers-siwe.tsx +++ b/apps/laboratory/src/pages/library/ethers-siwe.tsx @@ -21,6 +21,7 @@ import { } from '../../utils/ChainsUtil' import type { SIWECreateMessageArgs, SIWESession, SIWEVerifyMessageArgs } from '@web3modal/core' import { createSIWEConfig } from '@web3modal/siwe' +import { TestIdSiweAuthenticationStatus } from '../../constants' const projectId = process.env['NEXT_PUBLIC_PROJECT_ID'] if (!projectId) { @@ -133,7 +134,7 @@ export default function EthersSiwe() {
- SIWE Status: {status} + SIWE Status: {status} {session && ( <> Network: eip155:{session.chainId} diff --git a/apps/laboratory/src/pages/library/wagmi-siwe.tsx b/apps/laboratory/src/pages/library/wagmi-siwe.tsx index 18c0a5657b..82aa01a96f 100644 --- a/apps/laboratory/src/pages/library/wagmi-siwe.tsx +++ b/apps/laboratory/src/pages/library/wagmi-siwe.tsx @@ -23,6 +23,7 @@ import { NetworksButton } from '../../components/NetworksButton' import { ThemeStore } from '../../utils/StoreUtil' import type { SIWEVerifyMessageArgs, SIWECreateMessageArgs, SIWESession } from '@web3modal/core' import { createSIWEConfig } from '@web3modal/siwe' +import { TestIdSiweAuthenticationStatus } from '../../constants' // 1. Get projectId const projectId = process.env['NEXT_PUBLIC_PROJECT_ID'] @@ -145,7 +146,7 @@ export default function Wagmi() {
- Status: {status} + Status: {status} {session && ( <> Network: eip155:{session.chainId} diff --git a/apps/laboratory/tests/shared/fixtures/w3m-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-fixture.ts index a19d813a01..68e49bb8a2 100644 --- a/apps/laboratory/tests/shared/fixtures/w3m-fixture.ts +++ b/apps/laboratory/tests/shared/fixtures/w3m-fixture.ts @@ -13,7 +13,19 @@ export interface ModalFixture { export const testM = base.extend({ library: ['wagmi', { option: true }], modalPage: async ({ page, library }, use) => { - const modalPage = new ModalPage(page, library) + const modalPage = new ModalPage(page, library, 'default') + await modalPage.load() + await use(modalPage) + }, + modalValidator: async ({ modalPage }, use) => { + const modalValidator = new ModalValidator(modalPage.page) + await use(modalValidator) + } +}) +export const testMSiwe = base.extend({ + library: ['wagmi', { option: true }], + modalPage: async ({ page, library }, use) => { + const modalPage = new ModalPage(page, library, 'siwe') await modalPage.load() await use(modalPage) }, diff --git a/apps/laboratory/tests/shared/fixtures/w3m-wallet-fixture.ts b/apps/laboratory/tests/shared/fixtures/w3m-wallet-fixture.ts index 0c1a57067e..e8fab9c619 100644 --- a/apps/laboratory/tests/shared/fixtures/w3m-wallet-fixture.ts +++ b/apps/laboratory/tests/shared/fixtures/w3m-wallet-fixture.ts @@ -1,4 +1,4 @@ -import { testM as base } from './w3m-fixture' +import { testM as base, testMSiwe as siwe } from './w3m-fixture' import { WalletPage } from '../pages/WalletPage' import { WalletValidator } from '../validators/WalletValidator' @@ -26,4 +26,21 @@ export const testMW = base.extend({ await use(walletValidator) } }) +export const testMWSiwe = siwe.extend({ + walletPage: async ({ context, browserName }, use) => { + // WalletPage needs clipboard permissions with chromium to paste URI + if (browserName === 'chromium') { + await context.grantPermissions(['clipboard-read', 'clipboard-write']) + } + + // Use a new page, to open alongside the modal + const walletPage = new WalletPage(await context.newPage()) + await walletPage.load() + await use(walletPage) + }, + walletValidator: async ({ walletPage }, use) => { + const walletValidator = new WalletValidator(walletPage.page) + await use(walletValidator) + } +}) export { expect } from '@playwright/test' diff --git a/apps/laboratory/tests/shared/pages/ModalPage.ts b/apps/laboratory/tests/shared/pages/ModalPage.ts index bcfde2ce5b..10a94e3f03 100644 --- a/apps/laboratory/tests/shared/pages/ModalPage.ts +++ b/apps/laboratory/tests/shared/pages/ModalPage.ts @@ -1,24 +1,32 @@ import type { Locator, Page } from '@playwright/test' import { BASE_URL } from '../constants' +export type ModalFlavor = 'default' | 'siwe' + export class ModalPage { private readonly baseURL = BASE_URL private readonly connectButton: Locator + private readonly url: string constructor( public readonly page: Page, - public readonly library: string + public readonly library: string, + public readonly flavor: ModalFlavor ) { this.connectButton = this.page.getByTestId('connect-button') + this.url = + flavor === 'siwe' + ? `${this.baseURL}library/${this.library}-siwe/` + : `${this.baseURL}library/${this.library}/` } async load() { - await this.page.goto(`${this.baseURL}library/${this.library}/`) + await this.page.goto(this.url) } async copyConnectUriToClipboard() { - await this.page.goto(`${this.baseURL}library/${this.library}/`) + await this.page.goto(this.url) await this.connectButton.click() await this.page.getByTestId('wallet-selector-walletconnect').click() await this.page.waitForTimeout(2000) @@ -34,6 +42,14 @@ export class ModalPage { await this.page.getByTestId('sign-message-button').click() } + async promptSiwe() { + await this.page.getByTestId('w3m-connecting-siwe-sign').click() + } + + async cancelSiwe() { + await this.page.getByTestId('w3m-connecting-siwe-cancel').click() + } + async switchNetwork(network: string) { await this.page.getByTestId('account-button').click() await this.page.getByTestId('w3m-account-select-network').click() diff --git a/apps/laboratory/tests/shared/validators/ModalValidator.ts b/apps/laboratory/tests/shared/validators/ModalValidator.ts index 87bcc38082..243c277e05 100644 --- a/apps/laboratory/tests/shared/validators/ModalValidator.ts +++ b/apps/laboratory/tests/shared/validators/ModalValidator.ts @@ -9,6 +9,22 @@ export class ModalValidator { await expect(this.page.getByTestId('account-button')).toBeVisible() } + async expectAuthenticated() { + await expect(this.page.getByTestId('w3m-authentication-status')).toContainText( + 'Status: authenticated' + ) + } + + async expectUnauthenticated() { + await expect(this.page.getByTestId('w3m-authentication-status')).toContainText( + 'Status: unauthenticated' + ) + } + + async expectSignatureDeclined() { + await expect(this.page.getByText('Signature declined')).toBeVisible() + } + async expectDisconnected() { await expect(this.page.getByTestId('account-button')).not.toBeVisible() } diff --git a/apps/laboratory/tests/siwe.spec.ts b/apps/laboratory/tests/siwe.spec.ts new file mode 100644 index 0000000000..51ecb622d8 --- /dev/null +++ b/apps/laboratory/tests/siwe.spec.ts @@ -0,0 +1,38 @@ +import { DEFAULT_SESSION_PARAMS } from './shared/constants' +import { testMWSiwe } from './shared/fixtures/w3m-wallet-fixture' + +testMWSiwe.beforeEach(async ({ modalPage, walletPage }) => { + await modalPage.copyConnectUriToClipboard() + await walletPage.connect() + await walletPage.handleSessionProposal(DEFAULT_SESSION_PARAMS) +}) + +testMWSiwe.afterEach(async ({ modalValidator, walletValidator }) => { + await modalValidator.expectDisconnected() + await walletValidator.expectDisconnected() +}) + +testMWSiwe( + 'it should sign in with ethereum', + async ({ modalPage, walletPage, modalValidator, walletValidator }) => { + await modalPage.promptSiwe() + await walletValidator.expectReceivedSign({}) + await walletPage.handleRequest({ accept: true }) + await modalValidator.expectAuthenticated() + await modalValidator.expectConnected() + await walletValidator.expectConnected() + await modalPage.disconnect() + } +) + +testMWSiwe( + 'it should reject sign in with ethereum', + async ({ modalPage, walletPage, modalValidator, walletValidator }) => { + await modalPage.promptSiwe() + await walletValidator.expectReceivedSign({}) + await walletPage.handleRequest({ accept: false }) + await modalValidator.expectSignatureDeclined() + await modalPage.cancelSiwe() + await modalValidator.expectUnauthenticated() + } +) diff --git a/packages/scaffold/src/views/w3m-connecting-siwe-view/index.ts b/packages/scaffold/src/views/w3m-connecting-siwe-view/index.ts index f892155890..155e1d1079 100644 --- a/packages/scaffold/src/views/w3m-connecting-siwe-view/index.ts +++ b/packages/scaffold/src/views/w3m-connecting-siwe-view/index.ts @@ -45,7 +45,13 @@ export class W3mConnectingSiweView extends LitElement { > - + Cancel ${this.isSigning ? 'Signing...' : 'Sign'}