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(pom): cross-page chaining of page objects #27155

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
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
18 changes: 6 additions & 12 deletions test/e2e/page-objects/flows/login.flow.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import LoginPage from '../pages/login-page';
import HomePage from '../pages/homepage';
import { Driver } from '../../webdriver/driver';
import { DEFAULT_GANACHE_ETH_BALANCE_DEC } from '../../constants';
import { WALLET_PASSWORD } from '../../helpers';
import { getApp } from '../pages/app';
import HomePage from '../pages/homepage';

/**
* This method unlocks the wallet and verifies that the user lands on the homepage with the expected balance. It is designed to be the initial step in setting up a test environment.
Expand All @@ -15,16 +15,10 @@ export const loginWithBalanceValidation = async (
driver: Driver,
password: string = WALLET_PASSWORD,
expectedBalance: string = DEFAULT_GANACHE_ETH_BALANCE_DEC,
) => {
): Promise<HomePage> => {
console.log('Navigate to unlock page and try to login with pasword');
await driver.navigate();
const loginPage = new LoginPage(driver);
await loginPage.check_pageIsLoaded();
await loginPage.fillPassword(password);
await loginPage.clickUnlockButton();

// user should land on homepage after successfully logging in with password
const homePage = new HomePage(driver);
await homePage.check_pageIsLoaded();
const loginPage = await getApp(driver).getLoginPage();
const homePage = await loginPage.login(password);
await homePage.check_expectedBalanceIsDisplayed(expectedBalance);
return homePage;
};
14 changes: 4 additions & 10 deletions test/e2e/page-objects/flows/send-transaction.flow.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import HomePage from '../pages/homepage';
import ConfirmTxPage from '../pages/send/confirm-tx-page';
import SendTokenPage from '../pages/send/send-token-page';
import { Driver } from '../../webdriver/driver';

/**
* This function initiates the steps required to send a transaction from the homepage to final confirmation.
*
* @param driver - The webdriver instance.
* @param homePage
* @param recipientAddress - The recipient address.
* @param amount - The amount of the asset to be sent in the transaction.
* @param gasfee - The expected transaction gas fee.
* @param totalfee - The expected total transaction fee.
*/
export const sendTransaction = async (
driver: Driver,
homePage: HomePage,
recipientAddress: string,
amount: string,
gasfee: string,
Expand All @@ -23,18 +20,15 @@ export const sendTransaction = async (
`Start flow to send amount ${amount} to recipient ${recipientAddress} on home screen`,
);
// click send button on homepage to start flow
const homePage = new HomePage(driver);
await homePage.startSendFlow();
const sendToPage = await homePage.startSendFlow();

// user should land on send token screen to fill recipient and amount
const sendToPage = new SendTokenPage(driver);
await sendToPage.check_pageIsLoaded();
await sendToPage.fillRecipient(recipientAddress);
await sendToPage.fillAmount(amount);
await sendToPage.goToNextScreen();
const confirmTxPage = await sendToPage.continueToConfirmation();

// confirm transaction when user lands on confirm transaction screen
const confirmTxPage = new ConfirmTxPage(driver);
await confirmTxPage.check_pageIsLoaded(gasfee, totalfee);
await confirmTxPage.confirmTx();

Expand Down
33 changes: 33 additions & 0 deletions test/e2e/page-objects/pages/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Driver } from '../../webdriver/driver';
import { WALLET_PASSWORD } from '../../helpers';
import { BasePage } from './base-page';
import { HeaderNavbar } from './header-navbar';
import { SettingsPage } from './settings-page';
import HomePage from './homepage';
import LoginPage from './login-page';

// Top level page object for the app.
class App extends BasePage {
async getHeaderNavbar(): Promise<HeaderNavbar> {
// TODO: Ensure that the header navbar is visible.
return new HeaderNavbar(this.driver);
}

async getLoginPage(): Promise<LoginPage> {
return new LoginPage(this.driver).check_pageIsLoaded();
}

async openSettings(): Promise<SettingsPage> {
const headerNavbar = await this.getHeaderNavbar();
return headerNavbar.openSettings();
}

async login(password: string = WALLET_PASSWORD): Promise<HomePage> {
const loginPage = await this.getLoginPage();
return loginPage.login(password);
}
}

export const getApp = (driver: Driver): App => {
return new App(driver);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we really need to add this class. For the getHeaderNavbar() and getLoginPage() methods, we can create an instance of HeaderNavbar() or LoginPage() directly when we need them. For the openSettings(), we can do new HeaderNavbar(this.driver).openSettings() in tests. For the login method, I think the original loginWithBalanceValidation is more straightforward and efficient enough.
As page object model is in the adoption phase, we are trying to keep it as simple as possible to facilitate easier onboarding and usage.

9 changes: 9 additions & 0 deletions test/e2e/page-objects/pages/base-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Driver } from '../../webdriver/driver';

export class BasePage {
protected driver: Driver;

constructor(driver: Driver) {
this.driver = driver;
}
}
27 changes: 17 additions & 10 deletions test/e2e/page-objects/pages/header-navbar.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { Driver } from '../../webdriver/driver';
import { BasePage } from './base-page';
import { SettingsPage } from './settings-page';

class HeaderNavbar {
private driver: Driver;
export class HeaderNavbar extends BasePage {
private accountMenuButton: string = '[data-testid="account-menu-icon"]';

private accountMenuButton: string;
private settingsMenuButton: string = '[data-testid="global-menu-settings"]';

constructor(driver: Driver) {
this.driver = driver;
this.accountMenuButton = '[data-testid="account-menu-icon"]';
}
private globalMenuButton: string =
'[data-testid="account-options-menu-button"]';

async openAccountMenu(): Promise<void> {
await this.driver.clickElement(this.accountMenuButton);
}
}

export default HeaderNavbar;
async openGlobalMenu(): Promise<void> {
await this.driver.clickElement(this.globalMenuButton);
}

async openSettings(): Promise<SettingsPage> {
await this.openGlobalMenu();
await this.driver.clickElement(this.settingsMenuButton);
return new SettingsPage(this.driver);
}
}
52 changes: 18 additions & 34 deletions test/e2e/page-objects/pages/homepage.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,28 @@
import { strict as assert } from 'assert';
import { Driver } from '../../webdriver/driver';
import { DEFAULT_GANACHE_ETH_BALANCE_DEC } from '../../constants';
import HeaderNavbar from './header-navbar';
import SendTokenPage from './send/send-token-page';
import { BasePage } from './base-page';

class HomePage {
private driver: Driver;
export default class HomePage extends BasePage {
private sendButton = '[data-testid="eth-overview-send"]';

private sendButton: string;
private activityTab = '[data-testid="account-overview__activity-tab"]';

private activityTab: string;
private tokensTab = '[data-testid="account-overview__asset-tab"]';

private tokensTab: string;
private confirmedTransactions = {
text: 'Confirmed',
css: '.transaction-status-label--confirmed',
};

private balance: string;
private balance = '[data-testid="eth-overview__primary-currency"]';

private completedTransactions: string;
private completedTransactions = '[data-testid="activity-list-item"]';

private confirmedTransactions: object;
private transactionAmountsInActivity =
'[data-testid="transaction-list-item-primary-currency"]';

private transactionAmountsInActivity: string;

public headerNavbar: HeaderNavbar;

constructor(driver: Driver) {
this.driver = driver;
this.headerNavbar = new HeaderNavbar(driver);
this.sendButton = '[data-testid="eth-overview-send"]';
this.activityTab = '[data-testid="account-overview__activity-tab"]';
this.tokensTab = '[data-testid="account-overview__asset-tab"]';
this.confirmedTransactions = {
text: 'Confirmed',
css: '.transaction-status-label--confirmed',
};
this.balance = '[data-testid="eth-overview__primary-currency"]';
this.completedTransactions = '[data-testid="activity-list-item"]';
this.transactionAmountsInActivity =
'[data-testid="transaction-list-item-primary-currency"]';
}

async check_pageIsLoaded(): Promise<void> {
async check_pageIsLoaded(): Promise<HomePage> {
try {
await this.driver.waitForMultipleSelectors([
this.sendButton,
Expand All @@ -50,6 +34,7 @@ class HomePage {
throw e;
}
console.log('Home page is loaded');
return this;
}

async check_expectedBalanceIsDisplayed(
Expand All @@ -72,8 +57,9 @@ class HomePage {
);
}

async startSendFlow(): Promise<void> {
async startSendFlow(): Promise<SendTokenPage> {
await this.driver.clickElement(this.sendButton);
return new SendTokenPage(this.driver);
}

async goToActivityList(): Promise<void> {
Expand Down Expand Up @@ -161,5 +147,3 @@ class HomePage {
);
}
}

export default HomePage;
37 changes: 19 additions & 18 deletions test/e2e/page-objects/pages/login-page.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import { Driver } from '../../webdriver/driver';
import { WALLET_PASSWORD } from '../../helpers';
import HomePage from './homepage';
import { BasePage } from './base-page';

class LoginPage {
private driver: Driver;
class LoginPage extends BasePage {
private passwordInput = '[data-testid="unlock-password"]';

private passwordInput: string;
private unlockButton = '[data-testid="unlock-submit"]';

private unlockButton: string;
private welcomeBackMessage = {
css: '[data-testid="unlock-page-title"]',
text: 'Welcome back!',
};

private welcomeBackMessage: object;

constructor(driver: Driver) {
this.driver = driver;
this.passwordInput = '[data-testid="unlock-password"]';
this.unlockButton = '[data-testid="unlock-submit"]';
this.welcomeBackMessage = {
css: '[data-testid="unlock-page-title"]',
text: 'Welcome back!',
};
}

async check_pageIsLoaded(): Promise<void> {
async check_pageIsLoaded(): Promise<LoginPage> {
try {
await this.driver.waitForMultipleSelectors([
this.welcomeBackMessage,
Expand All @@ -31,6 +24,7 @@ class LoginPage {
throw e;
}
console.log('Login page is loaded');
return this;
}

async fillPassword(password: string): Promise<void> {
Expand All @@ -40,6 +34,13 @@ class LoginPage {
async clickUnlockButton(): Promise<void> {
await this.driver.clickElement(this.unlockButton);
}

// user lands on homepage after logging in with password
async login(password: string = WALLET_PASSWORD): Promise<HomePage> {
await this.fillPassword(password);
await this.clickUnlockButton();
return new HomePage(this.driver).check_pageIsLoaded();
}
}

export default LoginPage;
6 changes: 6 additions & 0 deletions test/e2e/page-objects/pages/send/send-token-page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { strict as assert } from 'assert';
import { Driver } from '../../../webdriver/driver';
import ConfirmTxPage from './confirm-tx-page';

class SendTokenPage {
private driver: Driver;
Expand Down Expand Up @@ -76,6 +77,11 @@ class SendTokenPage {
await this.driver.clickElement(this.continueButton);
}

async continueToConfirmation(): Promise<ConfirmTxPage> {
await this.goToNextScreen();
return new ConfirmTxPage(this.driver);
}

/**
* Verifies that an ENS domain correctly resolves to the specified Ethereum address on the send token screen.
*
Expand Down
Loading