Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: Add phantom support #1167

Closed
wants to merge 19 commits into from
Closed
19 changes: 19 additions & 0 deletions wallets/phantom/environment.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
CI: boolean
HEADLESS: boolean
}
}
}

declare global {
interface Window {
ethereum: import('ethers').Eip1193Provider
}

// biome-ignore lint/suspicious/noExplicitAny: Web3Mock is a mock object
const Web3Mock: any
}

export {}
54 changes: 54 additions & 0 deletions wallets/phantom/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@synthetixio/synpress-phantom",
"version": "0.0.1-alpha.0",
"type": "module",
"exports": {
"types": "./types/index.d.ts",
"default": "./dist/index.js"
},
"main": "./dist/index.js",
"types": "./types/index.d.ts",
"files": [
"dist",
"src",
"types"
],
"scripts": {
"build": "pnpm run clean && pnpm run build:dist && pnpm run build:types",
"build:cache": "synpress-cache test/wallet-setup --phantom",
"build:dist": "tsup --tsconfig tsconfig.build.json",
"build:types": "tsc --emitDeclarationOnly --project tsconfig.build.json",
"clean": "rimraf dist types",
"test:coverage": "vitest run --coverage",
"test:e2e:headful": "playwright test",
"test:e2e:headless": "HEADLESS=true playwright test",
"test:e2e:headless:ui": "HEADLESS=true playwright test --ui",
"test:watch": "vitest watch",
"types:check": "tsc --noEmit"
},
"dependencies": {
"@synthetixio/synpress-cache": "workspace:*",
"@synthetixio/synpress-core": "workspace:*",
"@viem/anvil": "0.0.7",
"clipboardy": "4.0.0",
"fs-extra": "11.2.0",
"node-fetch": "3.3.2",
"underscore": "1.13.6",
"zod": "3.22.4"
},
"devDependencies": {
"@synthetixio/synpress-tsconfig": "workspace:*",
"@types/fs-extra": "11.0.4",
"@types/node": "20.11.17",
"@types/underscore": "1.11.15",
"@vitest/coverage-v8": "1.2.2",
"cypress": "13.9.0",
"rimraf": "5.0.5",
"tsup": "8.0.2",
"typescript": "5.3.3",
"vitest": "1.2.2"
},
"peerDependencies": {
"@playwright/test": "1.44.0"
}
}
50 changes: 50 additions & 0 deletions wallets/phantom/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { defineConfig, devices } from '@playwright/test'

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
// Look for test files in the "test/e2e" directory, relative to this configuration file.
testDir: './test/e2e',

// We're increasing the timeout to 60 seconds to allow all traces to be recorded.
// Sometimes it threw an error saying that traces were not recorded in the 30 seconds timeout limit.
timeout: 60_000,

// Run all tests in parallel.
fullyParallel: true,

// Fail the build on CI if you accidentally left test.only in the source code.
forbidOnly: !!process.env.CI,

// Fail all remaining tests on CI after the first failure. We want to reduce the feedback loop on CI to minimum.
maxFailures: process.env.CI ? 1 : 0,

// Opt out of parallel tests on CI since it supports only 1 worker.
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
? [['html', { open: 'never', outputFolder: `playwright-report-${process.env.HEADLESS ? 'headless' : 'headful'}` }]]
: 'html',

// Shared settings for all the projects below.
// See https://playwright.dev/docs/api/class-testoptions.
use: {
// We are using locally deployed MetaMask Test Dapp.
baseURL: '/',

// Collect all traces on CI, and only traces for failed tests when running locally.
// See https://playwright.dev/docs/trace-viewer.
trace: process.env.CI ? 'on' : 'retain-on-failure'
},

// Configure projects for major browsers.
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
]
})
58 changes: 58 additions & 0 deletions wallets/phantom/src/PhantomWallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { type BrowserContext, type Page } from '@playwright/test'
import { HomePage } from './pages/HomePage/page'
import { LockPage } from './pages/LockPage/page'
import { NotificationPage } from './pages/NotificationPage/page'

export class KeplrWallet {
readonly lockPage: LockPage
readonly homePage: HomePage
readonly notificationPage: NotificationPage

constructor(
readonly page: Page,
readonly context?: BrowserContext,
readonly password?: string,
readonly extensionId?: string | undefined
) {
this.lockPage = new LockPage(page)
this.homePage = new HomePage(page)
this.notificationPage = new NotificationPage(page)
Fixed Show fixed Hide fixed
}
/**
* Does initial setup for the wallet.
*
* @param playwrightInstance. The playwright instance to use.
* @param secretWordsOrPrivateKey. The secret words or private key to import.
* @param password. The password to set.
*/
async setupWallet({ secretWordsOrPrivateKey, password }: { secretWordsOrPrivateKey: string; password: string }) {
const wallet = await this.lockPage.importWallet(secretWordsOrPrivateKey, password)
return wallet
}

async createWallet(password: string) {
const wallet = await this.lockPage.createWallet(password)
return wallet
}

async getWalletAddress(wallet: string) {
const walletAddress = await this.homePage.getWalletAddress(wallet)
return walletAddress
}

async addNewTokensFound() {
return await this.homePage.addNewTokensFound()
}

async disconnectWalletFromDapps() {
return await this.homePage.disconnectWalletFromDapps()
}

async acceptAccess() {
return await this.notificationPage.acceptAccess()
}

async rejectAccess() {
return await this.notificationPage.rejectAccess()
}
}
3 changes: 3 additions & 0 deletions wallets/phantom/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './PhantomWallet'
export * from './utils'
export * from './fixtures/phantomFixtures'
File renamed without changes.
13 changes: 13 additions & 0 deletions wallets/phantom/src/pages/ConfirmationPage/page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Page } from '@playwright/test'
import { confirmationPageElements } from './selectors'

export class ConfirmationPage {
static readonly selectors = confirmationPageElements
readonly selectors = confirmationPageElements

readonly page: Page

constructor(page: Page) {
this.page = page
}
}
12 changes: 12 additions & 0 deletions wallets/phantom/src/pages/ConfirmationPage/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const confirmationPage = '.confirmation-page';
const confirmationPageFooter = `${confirmationPage} .confirmation-footer`;
const footer = {
footer: confirmationPageFooter,
cancelButton: `${confirmationPageFooter} .btn-secondary`,
approveButton: `${confirmationPageFooter} .btn-primary`,
};

export const confirmationPageElements = {
confirmationPage,
footer,
};
Empty file.
13 changes: 13 additions & 0 deletions wallets/phantom/src/pages/HomePage/page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Page } from '@playwright/test'
import { homePageElements } from './selectors'

export class HomePage {
static readonly selectors = homePageElements
readonly selectors = homePageElements

readonly page: Page

constructor(page: Page) {
this.page = page
}
}
165 changes: 165 additions & 0 deletions wallets/phantom/src/pages/HomePage/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
const networkSwitcherButtonSelector = '.network-display';

const networkSwitcher = {
button: networkSwitcherButtonSelector,
networkName: `${networkSwitcherButtonSelector} .typography`,
dropdownMenu: '[data-testid="network-droppo"]',
dropdownMenuItem: `[data-testid="network-droppo"] .dropdown-menu-item`,
mainnetNetworkItem: `[data-testid="network-droppo"] [data-testid="mainnet-network-item"]`,
goerliNetworkItem: `[data-testid="network-droppo"] [data-testid="goerli-network-item"]`,
sepoliaNetworkItem: `[data-testid="network-droppo"] [data-testid="sepolia-network-item"]`,
localhostNetworkItem: `[data-testid="network-droppo"] [data-testid="Localhost 8545-network-item"]`,
networkButton: number =>
`[data-testid="network-droppo"] .dropdown-menu-item:nth-child(${
3 + number
})`,
};


const walletOverview = '.wallet-overview';

const tabs = {
assetsButton: '[data-testid="home__asset-tab"] button',
activityButton: '[data-testid="home__activity-tab"] button',
};

const transactionList = '.transaction-list__transactions';
const pendingTransactionsList = `${transactionList} .transaction-list__pending-transactions`;
const completedTransactionsList = `${transactionList} .transaction-list__completed-transactions`;
const activityTab = {
transactionList,
pendingTransactionsList,
completedTransactionsList,
unconfirmedTransaction: `${pendingTransactionsList} .transaction-list-item--unconfirmed`,
confirmedTransaction: `${completedTransactionsList} .transaction-list-item`,
};

const popupSelector = '.popover-container';
const sendPopupSelector = `${popupSelector} .transaction-list-item-details`;
const popup = {
container: popupSelector,
closeButton: '.popover-header__button',
background: '.popover-bg',
sendPopup: {
container: sendPopupSelector,
speedUpButton: `${sendPopupSelector} .btn-primary`,
cancelButton: `${sendPopupSelector} .btn-secondary`,
transactionStatus: `${sendPopupSelector} .transaction-status`,
copyTxIdButton: `${sendPopupSelector} .transaction-list-item-details__tx-hash .transaction-list-item-details__header-button a`,
},
};

const tippyTooltipSelector = '.tippy-popper';
const tippyTooltip = {
container: tippyTooltipSelector,
closeButton: `${tippyTooltipSelector} button`,
};

const actionableMessageSelector = '.actionable-message';
const actionableMessage = {
container: actionableMessageSelector,
closeButton: `${actionableMessageSelector} button`,
};

const accountMenu = {
accountButton: number => `[data-testid="account-menu"] [data-testid="tooltip_interactive-wrapper"]:nth-child(${number})`,
};

const settingsMenu = {
settingsMenuButton: '[data-testid="settings-menu-open-button"]',
settingsSidebarButton: '[data-testid="sidebar_menu-button-settings"]',
settingsSidebarCloseButton: '[data-testid="settings-menu-close-button"]',
settingsPreferencesButton: '[data-testid="settings-item-preferences"]',
trustedAppsRow: '[data-testid="settings-item-trusted-apps"]',
developerSettingsRow: '[data-testid="settings-item-developer-settings"]',
defaultAppWalletRow: '[data-testid="settings-item-metamask-override"]',
};
const whatsNew = {
header: '[data-testid="whats_new-header"]',
continueButton: '[data-testid="whats_new-continue_button"]',
};

const welcome = {
takeTheTourButton: '[data-testid="welcome-take_the_tour"]',
takeTheTourButtonNext: '[data-testid="primary-button"]',
finishSetup: ['data-testid="onboarding-form-submit-button"'],
};

const accountBar = {
title: '[data-testid="tooltip_interactive-wrapper"]',
ethRow: '[data-testid="account-header-chain-eip155:1"]',
solanaRow: '[data-testid="account-header-chain-solana:101"]',
};

const defaultWallet = {
metamask: '[data-testid="metamask-override--USE_METAMASK"]',
phantom: '[data-testid="metamask-override--USE_PHANTOM"]',
always_ask: '[data-testid="metamask-override--ALWAYS_ASK"]',
};

const connectedSites = {
trustedAppsRevokeButton: '[data-testid="trusted-apps-revoke-button"]',
trustedAppsBackButton: '[data-testid="header--back"]',
rowButton: '[data-testid="trusted_apps_row-button"]',
};

const accountModal = {
walletAddressInput: '.account-modal .qr-code__address',
closeButton: '.account-modal__close',
};

const importAccountSelector = '.new-account';
const importAccount = {
page: importAccountSelector,
input: `${importAccountSelector} #private-key-box`,
cancelButton: `${importAccountSelector} .new-account-create-form__button:nth-child(1)`,
importButton: `${importAccountSelector} .new-account-create-form__button:nth-child(2)`,
};

const createAccount = {
page: importAccountSelector,
input: `${importAccountSelector} .new-account-create-form__input`,
cancelButton: `${importAccountSelector} .new-account-create-form__button:nth-child(1)`,
createButton: `${importAccountSelector} .new-account-create-form__button:nth-child(2)`,
};

const importTokenFormSelector = '.import-token__custom-token-form';
const importToken = {
form: importTokenFormSelector,
tokenContractAddressInput: `${importTokenFormSelector} #custom-address`,
tokenSymbolInput: `${importTokenFormSelector} #custom-symbol`,
tokenEditButton: `${importTokenFormSelector} .import-token__custom-symbol__edit`,
tokenDecimalInput: `${importTokenFormSelector} #custom-decimals`,
addCustomTokenButton: `[data-testid="page-container-footer-next"]`,
confirmImportTokenContent: '.confirm-import-token',
importTokensButton: `.btn-primary`,
};

const assetNavigationSelector = '.asset-navigation';
const asset = {
navigation: assetNavigationSelector,
backButton: `${assetNavigationSelector} [data-testid="asset__back"]`,
};


export const homePageElements = {
networkSwitcher,
importToken,
importAccount,
createAccount,
accountMenu,
accountBar,
asset,
tabs,
walletOverview,
accountModal,
connectedSites,
welcome,
defaultWallet,
whatsNew,
popup,
activityTab,
settingsMenu,
actionableMessage,
tippyTooltip
}
Fixed Show fixed Hide fixed
Empty file.
Loading
Loading