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

test(e2e): Solana Phantom Wallet #433

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
36ff641
base
nelitow Nov 19, 2024
e36c591
Merge branch 'main' into nj/tests/phantom-solana
nelitow Nov 21, 2024
70dcca0
chore: clean up package.json by removing unused dependencies
nelitow Nov 21, 2024
f9d29d2
chore(e2e): update @phantom/synpress dependency to version 4.0.0-alph…
nelitow Nov 21, 2024
b66adeb
chore(e2e): update TypeScript configuration to use ES2015 module
nelitow Nov 21, 2024
d0f1ac8
test(e2e): refactor PhantomSolanaConnector tests to integrate Phantom…
nelitow Nov 21, 2024
01cee86
feat(e2e): add PhantomSolanaConnector setup and utility functions for…
nelitow Nov 21, 2024
1fa406d
test(e2e): enhance PhantomSolanaConnector tests with address validati…
nelitow Nov 21, 2024
67faf32
chore(e2e): remove ES2015 module setting from TypeScript configuration
nelitow Nov 21, 2024
f263c4a
test(e2e): add timeout before transfer tests in PhantomSolanaConnector
nelitow Nov 21, 2024
52c75ab
duh
nelitow Nov 21, 2024
430f16a
test(e2e): comment out unused transfer approval and funding logic in …
nelitow Nov 21, 2024
4c2b09c
test(e2e): clean up PhantomSolanaConnector tests by removing commente…
nelitow Nov 22, 2024
004716f
test(e2e): clean up PhantomSolanaConnector setup by removing commente…
nelitow Nov 22, 2024
37f3dad
refactor(e2e): simplify waitForExtensions function by removing unnece…
nelitow Nov 22, 2024
08756e0
chore(e2e): reorder dependencies in package.json for consistency
nelitow Nov 22, 2024
6e43fcd
Update PhantomSolanaConnector.test.ts
nelitow Nov 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
expect,
getButtonByText,
getByAriaLabel,
} from '@fuels/playwright-utils';
import type { Page } from '@playwright/test';
import { testWithSynpress } from '@synthetixio/synpress';
import { sessionTests, transferTests } from '../../../common/common';
// import * as phantom from "@phantom/synpress/commands/phantom";
import phantom from '../../../node_modules/@phantom/synpress/commands/phantom';
import basicSetup from '../../../wallet-setup/basic.setup';
import { fundWallet } from '../setup';
import { test } from './setup';

phantom.confirmTransaction = async () => {
const notificationPage =
await phantom.playwright.switchToNotification('phantom');
await phantom.playwright.waitAndClick(
'phantom',
phantom.transactionPageElements.buttons.confirmTransaction, // Ensure this locator exists or define it
notificationPage,
{ waitForEvent: 'close' },
);
return true;
};

test.describe('PhantomSolanaConnector', () => {
test.slow();

const connect = async (page: Page) => {
await page.goto('/');
const connectButton = getButtonByText(page, 'Connect Wallet', true);
await connectButton.click();
await getByAriaLabel(page, 'Connect to Solana Wallets', true).click();

nelitow marked this conversation as resolved.
Show resolved Hide resolved
await page.getByText('Proceed anyway').click();
await getButtonByText(page, 'Phantom').click();

await phantom.acceptAccess();
await page.waitForTimeout(3000);
};

// const approveTransfer = async () => {
// await phantom.confirmTransaction();
// };

test('Fuel tests', async ({ page }) => {
// await sessionTests(page, { connect, approveTransfer });

await connect(page);
const addressElement = await page.locator('#address');
let address = null;
if (addressElement) {
address = await addressElement.getAttribute('data-address');
}

test.step('Check if address is not null', () => {
expect(address).not.toBeNull();
});

// if (address) {
nelitow marked this conversation as resolved.
Show resolved Hide resolved
// await fundWallet({ publicKey: address });
// } else {
// throw new Error('Address is null');
// }

// await transferTests(page, { connect, approveTransfer, keepSession: true });

// await incrementTests(page, { connect, approveTransfer });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mocks';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const ETH_MNEMONIC =
'test test test test test test test test test test test junk';
export const ETH_WALLET_PASSWORD = 'Tester@1234';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Use a test fixture to set the context so tests have access to the wallet extension.
import { downloadFuel } from '@fuels/playwright-utils';
import type { BrowserContext } from '@playwright/test';
import { test as base, chromium } from '@playwright/test';
import phantomCommands from '../../../node_modules/@phantom/synpress/commands/phantom';
import phantomHelpers from '../../../node_modules/@phantom/synpress/helpers';

import { ETH_MNEMONIC, ETH_WALLET_PASSWORD } from './mocks';
import { getExtensionsData } from './utils/getExtensionsData';
import { waitForExtensions } from './utils/waitForExtensions';

export const test = base.extend<{
context: BrowserContext;
extensionId: string;
}>({
context: async ({ context: _ }, use) => {
// required for synpress
global.expect = expect;
// download fuel wallet
// download metamask
const phantomPath = await phantomHelpers.prepareProvider(
'phantom',
'latest',
);
// prepare browser args
const browserArgs = [
`--disable-extensions-except=${phantomPath}`,
`--load-extension=${phantomPath}`,
'--remote-debugging-port=9222',
];
// launch browser
nelitow marked this conversation as resolved.
Show resolved Hide resolved
const context = await chromium.launchPersistentContext('', {
headless: false,
args: browserArgs,
});

const extensions = await getExtensionsData(context);
// Wait for Wallet to load
await waitForExtensions(context, extensions);
// Setup cynpress MetaMask

await phantomCommands.initialSetup(chromium, {
secretWordsOrPrivateKey: ETH_MNEMONIC,
network: 'localhost',
password: ETH_WALLET_PASSWORD,
enableAdvancedSettings: true,
enableExperimentalSettings: false,
});
// Set context to playwright
await use(context);
},
extensionId: async ({ context }, use) => {
let [background] = context.serviceWorkers();
if (!background) background = await context.waitForEvent('serviceworker');
const extensionId = background.url().split('/')[2];
await use(extensionId);
},
});

export const expect = test.expect;
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* This code is required to avoid synpress to start without
* the extensions be ready making it fail.
*
* This code was copied from the synpress codebase to be used as a standalone function.
* https://github.com/Synthetixio/synpress/blob/81faa920fb683b1b579fde5214f923c758877157/commands/playwright.js#L445
*/

import type { BrowserContext } from '@playwright/test';

export async function getExtensionsData(context: BrowserContext) {
const extensionsData = {};
const page = await context.newPage();

await page.goto('chrome://extensions');
await page.waitForLoadState('load');
await page.waitForLoadState('domcontentloaded');

const devModeButton = page.locator('#devMode');
await devModeButton.waitFor();
await devModeButton.focus();
await devModeButton.click();

const extensionDataItems = await page.locator('extensions-item').all();
for (const extensionData of extensionDataItems) {
const extensionName = (
await extensionData
.locator('#name-and-version')
.locator('#name')
.textContent()
).toLowerCase();

const extensionVersion = (
await extensionData
.locator('#name-and-version')
.locator('#version')
.textContent()
).replace(/(\n| )/g, '');

const extensionId = (
await extensionData.locator('#extension-id').textContent()
).replace('ID: ', '');

extensionsData[extensionName] = {
version: extensionVersion,
id: extensionId,
};
}
await page.close();

return extensionsData;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { setTimeout } from 'node:timers/promises';
import type { BrowserContext } from '@playwright/test';

export async function waitForExtensions(
context: BrowserContext,
extensions: Record<
string,
{
id: string;
version: string;
}
>,
maxAttempts = 5,
attempt = 0,
): Promise<boolean> {
if (!context) throw new Error('BrowserContext is required.');
if (!extensions || typeof extensions !== 'object') {
throw new Error('Invalid extensions object provided.');
}

console.log(`Checking extensions (Attempt ${attempt + 1}/${maxAttempts})...`);
const pages = context.pages();

if (!pages.length) {
console.warn('No pages found in the context. Retrying...');
}

const phantomPage = pages.find((page) =>
page.url().includes(extensions.phantom?.id),
);

nelitow marked this conversation as resolved.
Show resolved Hide resolved
if (phantomPage) {
console.log('Phantom extension is ready!');
return true;
}

if (attempt >= maxAttempts - 1) {
throw new Error(
`Failed to detect extensions after ${maxAttempts} attempts.`,
);
}

console.log('Phantom extension not found. Retrying in 3 seconds...');
await setTimeout(3000);
return waitForExtensions(context, extensions, maxAttempts, attempt + 1);
}
5 changes: 4 additions & 1 deletion e2e-tests/runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"test:react-app": "pnpm test:react-app:ci -- --ui",
"test:react-app:ci": "playwright test e2e-tests/react-app --project=react-app",
"test:react-next": "pnpm playwright test --project=react-next",
"test:e2e:dev": "pnpm --workspace-root node:up && pnpm run start:servers & pnpm playwright test --ui",
"test:e2e:local": "pnpm run deploy:contracts && pnpm run test:e2e",
"start:react-app": "pnpm --filter react-app dev",
"start:react-next": "pnpm --filter @e2e-tests/react-next dev",
"start:servers": "pnpm run start:react-app & pnpm run start:react-next",
Expand All @@ -25,7 +27,8 @@
"@synthetixio/synpress": "4.0.3",
"@synthetixio/synpress-cache": "0.0.4",
"dotenv": "16.4.5",
"fuels": "0.96.1"
"fuels": "0.96.1",
"@phantom/synpress": "4.0.0-alpha.53"
},
"engines": {
"node": ">=20.11.0",
Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,5 @@
"fast-xml-parser@<4.4.1": ">=4.4.1"
}
},
"packageManager": "[email protected]",
"dependencies": {
"@synthetixio/synpress": "4.0.3"
}
"packageManager": "[email protected]"
}
Loading
Loading