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

Ctrl wallet #210

Merged
merged 15 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: yarn build

- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: yarn playwright install chromium --with-deps

- name: Run wallets tests
run: xvfb-run --auto-servernum -- yarn test:widgets
Expand All @@ -42,12 +42,12 @@ jobs:
WALLET_PASSWORD: ${{ secrets.WALLET_PASSWORD }}
NODE_OPTIONS: --max-old-space-size=4096

- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: playwright-report
path: wallets-testing/playwright-report/
retention-days: 30
retention-days: 7

- name: Set embeds
if: ${{ always() }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { CommonWalletConfig } from '../wallets.constants';

export const XDEFI_COMMON_CONFIG: CommonWalletConfig = {
WALLET_NAME: 'xdefi',
CONNECTED_WALLET_NAME: 'XDEFI',
export const CTRL_COMMON_CONFIG: CommonWalletConfig = {
WALLET_NAME: 'ctrl',
CONNECTED_WALLET_NAME: 'Ctrl',
RPC_URL_PATTERN: 'https://mainnet.infura.io/v3/**',
STORE_EXTENSION_ID: 'hmeobnfnfcmdkdcmlblgagmfpfboieaf',
CONNECT_BUTTON_NAME: 'XDEFI',
CONNECT_BUTTON_NAME: 'Ctrl',
SIMPLE_CONNECT: false,
EXTENSION_START_PATH: '/app.html',
EXTENSION_START_PATH: '/popup.html',
};
135 changes: 135 additions & 0 deletions packages/wallets/src/ctrl/ctrl.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { WalletPage } from '../wallet.page';
import { test, BrowserContext, Page } from '@playwright/test';
import { WalletConfig } from '../wallets.constants';
import { LoginPage, OnboardingPage, WalletOperations } from './pages';

export class CtrlPage implements WalletPage {
page: Page | undefined;
onboardingPage: OnboardingPage;
loginPage: LoginPage;

constructor(
private browserContext: BrowserContext,
private extensionUrl: string,
public config: WalletConfig,
) {}

/** Init all page objects Classes included to wallet */
async initLocators() {
this.page = await this.browserContext.newPage();
this.onboardingPage = new OnboardingPage(
this.page,
this.extensionUrl,
this.config,
);
this.loginPage = new LoginPage(this.page, this.config);
}

/** Open the home page of the wallet extension */
async goto() {
await this.page.goto(
this.extensionUrl + this.config.COMMON.EXTENSION_START_PATH,
);
}

/** Navigate to home page of OXK Wallet extension:
* - open the wallet extension
* - unlock extension (if needed)
*/
async navigate() {
await test.step('Navigate to Ctrl', async () => {
await this.initLocators();
await this.goto();
await this.loginPage.unlock();
});
}

async setup() {
await test.step('Setup', async () => {
await this.initLocators();
await this.onboardingPage.firstTimeSetup();
});
}

/** Click `Connect` button */
async connectWallet(page: Page) {
await test.step('Connect Ctrl wallet', async () => {
const operationPage = new WalletOperations(page);
await operationPage.connectBtn.waitFor({
state: 'visible',
timeout: 10000,
});
await operationPage.connectBtn.click();
// need wait the page to be closed after the extension is connected
await new Promise<void>((resolve) => {
operationPage.page.on('close', () => {
resolve();
});
});
});
}

importKey(): Promise<void> {
throw new Error('Method not implemented.');
}

assertTxAmount(): Promise<void> {
throw new Error('Method not implemented.');
}

confirmTx(): Promise<void> {
throw new Error('Method not implemented.');
}

cancelTx(): Promise<void> {
throw new Error('Method not implemented.');
}

approveTokenTx(): Promise<void> {
throw new Error('Method not implemented.');
}

openLastTxInEthplorer(): Promise<Page> {
throw new Error('Method not implemented.');
}

getTokenBalance(): Promise<number> {
throw new Error('Method not implemented.');
}

confirmAddTokenToWallet(): Promise<void> {
throw new Error('Method not implemented.');
}

assertReceiptAddress(): Promise<void> {
throw new Error('Method not implemented.');
}

getWalletAddress(): Promise<string> {
throw new Error('Method not implemented.');
}

setupNetwork(): Promise<void> {
throw new Error('Method not implemented.');
}

addNetwork(): Promise<void> {
throw new Error('Method not implemented.');
}

changeNetwork(): Promise<void> {
throw new Error('Method not implemented.');
}

changeWalletAccountByName(): Promise<void> {
throw new Error('Method not implemented.');
}

changeWalletAccountByAddress(): Promise<void> {
throw new Error('Method not implemented.');
}

isWalletAddressExist(): Promise<boolean> {
throw new Error('Method not implemented.');
}
}
2 changes: 2 additions & 0 deletions packages/wallets/src/ctrl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './ctrl.page';
export * from './ctrl.constants';
3 changes: 3 additions & 0 deletions packages/wallets/src/ctrl/pages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './onboarding.page';
export * from './login.page';
export * from './walletOperations.page';
28 changes: 28 additions & 0 deletions packages/wallets/src/ctrl/pages/login.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Locator, Page, test } from '@playwright/test';
import { WalletConfig } from '../../wallets.constants';

export class LoginPage {
page: Page;
unlockBtn: Locator;
passwordInput: Locator;
homeBtn: Locator;

constructor(page: Page, public config: WalletConfig) {
this.page = page;
this.unlockBtn = this.page.getByTestId('unlock-btn');
this.passwordInput = this.page.locator('input[type="password"]');
}

async unlock() {
await test.step('Unlock wallet', async () => {
try {
await this.unlockBtn.waitFor({ state: 'visible', timeout: 2000 });
await this.passwordInput.fill(this.config.PASSWORD);
await this.unlockBtn.click();
await this.homeBtn.waitFor({ state: 'visible' });
} catch {
console.log('The Wallet unlocking is not needed');
}
});
}
}
100 changes: 100 additions & 0 deletions packages/wallets/src/ctrl/pages/onboarding.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Locator, Page, test, expect } from '@playwright/test';
import { WalletConfig } from '../../wallets.constants';

export class OnboardingPage {
page: Page;
alreadyHaveWalletBtn: Locator;
importRecoveryPhraseBtn: Locator;
passwordInput: Locator;
nextBtn: Locator;
importBtn: Locator;
closeTourBtn: Locator;
notNowBtn: Locator;
createPasswordBtn: Locator;
confirmPasswordBtn: Locator;

constructor(
page: Page,
private extensionUrl: string,
public config: WalletConfig,
) {
this.page = page;
this.alreadyHaveWalletBtn = this.page.getByTestId(
'i-already-have-a-wallet-btn',
);
this.importRecoveryPhraseBtn = this.page.getByText(
'Import with Recovery Phrase',
);
this.passwordInput = this.page.locator('input[type="password"]');
this.nextBtn = this.page.getByTestId('next-btn');
this.importBtn = this.page.getByTestId('import-btn');
this.closeTourBtn = this.page.locator(
'[testID="home-page-tour-close-icon"]',
);
this.notNowBtn = this.page.getByText('Not now');
this.createPasswordBtn = this.page.getByText('create a password');
this.confirmPasswordBtn = this.page.getByTestId('button');
}

async firstTimeSetup() {
if (await this.isWalletSetup()) return;

await test.step('First time set up', async () => {
// Without additional awaiting the extension breaks the next step and redirects a user back
await this.page.waitForTimeout(2000);
await this.alreadyHaveWalletBtn.click({ force: true });

await test.step('Import wallet with recovery phrase', async () => {
await this.importRecoveryPhraseBtn.click();
const seedWords = this.config.SECRET_PHRASE.split(' ');
for (let i = 0; i < seedWords.length; i++) {
await this.passwordInput.nth(i).fill(seedWords[i]);
}
await this.nextBtn.click();
await this.importBtn.waitFor({ state: 'visible', timeout: 60000 });
await expect(this.importBtn).toBeEnabled({ timeout: 2000 });
await this.importBtn.click();
await this.nextBtn.click();
});

await this.page.goto(
this.extensionUrl + this.config.COMMON.EXTENSION_START_PATH,
);

await test.step('Close wallet tour', async () => {
await this.closeTourBtn.waitFor({ state: 'visible', timeout: 2000 });
await this.closeTourBtn.click();
await this.notNowBtn.waitFor({ state: 'visible', timeout: 2000 });
await this.page.waitForTimeout(1000); // Need to wait some time for button enabling
await this.notNowBtn.click({ force: true });
});

await test.step('Create wallet password', async () => {
await this.createPasswordBtn.click();
await this.passwordInput.nth(0).fill(this.config.PASSWORD);
await this.passwordInput.nth(1).fill(this.config.PASSWORD);
await this.confirmPasswordBtn.click();
});
});
}

async isWalletSetup() {
await test.step('Open the onboarding page', async () => {
// Need to open onboarding page cause the extension does not redirect from home url automatically
await this.page.goto(
this.extensionUrl + '/tabs/onboarding.html#onboarding',
);
});
return await test.step('Check the wallet is set up', async () => {
try {
await this.alreadyHaveWalletBtn.waitFor({
state: 'visible',
timeout: 5000,
});
} catch {
console.log('Ctrl wallet: Onboarding process is not needed');
}
return !(await this.alreadyHaveWalletBtn.isVisible());
});
}
}
11 changes: 11 additions & 0 deletions packages/wallets/src/ctrl/pages/walletOperations.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Locator, Page } from '@playwright/test';

export class WalletOperations {
page: Page;
connectBtn: Locator;

constructor(page: Page) {
this.page = page;
this.connectBtn = this.page.getByTestId('connect-dapp-button');
}
}
2 changes: 1 addition & 1 deletion packages/wallets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export * from './metamask';
export * from './trustwallet';
export * from './coinbase';
export * from './exodus';
export * from './xdefi';
export * from './okx';
export * from './bitget';
export * from './ctrl';
2 changes: 1 addition & 1 deletion packages/wallets/src/okx/okx.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class OkxPage implements WalletPage {
});
}

/** Checks the wallet is set correctly and starts ot wallet setup as the first time (if needed) */
/** Checks the wallet is set correctly and starts the wallet setup as the first time (if needed) */
async setup() {
await test.step('Setup', async () => {
await this.navigate();
Expand Down
2 changes: 0 additions & 2 deletions packages/wallets/src/xdefi/index.ts

This file was deleted.

Loading
Loading