Skip to content

Commit

Permalink
test: [POM] Migrate Snap Account Settings to page object model (#27302)
Browse files Browse the repository at this point in the history
## **Description**

This pull request migrate snap account settings test to page object
model(POM) pattern, enhancing code maintainability, and improving test
reliability.
Also improved the implementation of login methods so we query directly
ganache, and there's no need to pass any balance manually.

## **Description**

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27155?quickstart=1)

## **Related issues**

Fixes: #27343  

## **Manual testing steps**
Check code readability, make sure tests pass.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Chloe Gao <[email protected]>
Co-authored-by: chloeYue <[email protected]>
  • Loading branch information
3 people authored Sep 25, 2024
1 parent fce2e8b commit 7054c16
Show file tree
Hide file tree
Showing 18 changed files with 333 additions and 176 deletions.
62 changes: 0 additions & 62 deletions test/e2e/accounts/snap-account-settings.spec.ts

This file was deleted.

38 changes: 26 additions & 12 deletions test/e2e/page-objects/flows/login.flow.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
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 { Ganache } from '../../seeder/ganache';

/**
* 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.
* This method unlocks the wallet and lands the user on the homepage.
*
* @param driver - The webdriver instance.
* @param password - The password used to unlock the wallet. Defaults to WALLET_PASSWORD.
* @param expectedBalance - The expected balance to be displayed on the homepage after successful login. Defaults to DEFAULT_GANACHE_ETH_BALANCE_DEC, reflecting common usage in test setups.
* @param password - The password used to unlock the wallet.
*/
export const loginWithBalanceValidation = async (
export const loginWithoutBalanceValidation = async (
driver: Driver,
password: string = WALLET_PASSWORD,
expectedBalance: string = DEFAULT_GANACHE_ETH_BALANCE_DEC,
password?: string,
) => {
console.log('Navigate to unlock page and try to login with pasword');
console.log('Navigate to unlock page and try to login with password');
await driver.navigate();
const loginPage = new LoginPage(driver);
await loginPage.check_pageIsLoaded();
await loginPage.fillPassword(password);
await loginPage.clickUnlockButton();
await loginPage.loginToHomepage(password);

// user should land on homepage after successfully logging in with password
const homePage = new HomePage(driver);
await homePage.check_pageIsLoaded();
await homePage.check_expectedBalanceIsDisplayed(expectedBalance);
};

/**
* 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.
*
* @param driver - The webdriver instance.
* @param ganacheServer - The ganache server instance
* @param password - The password used to unlock the wallet.
*/
export const loginWithBalanceValidation = async (
driver: Driver,
ganacheServer?: Ganache,
password?: string,
) => {
await loginWithoutBalanceValidation(driver, password);
// Verify the expected balance on the homepage
if (ganacheServer) {
await new HomePage(driver).check_ganacheBalanceIsDisplayed(ganacheServer);
}
};
119 changes: 74 additions & 45 deletions test/e2e/page-objects/pages/account-list-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,65 @@ class AccountListPage {

private accountListItem: string;

private accountOptionsMenuButton: string;
private accountMenuButton: string;

private hideUnhideAccountButton: string;
private accountNameInput: string;

private hiddenAccountsList: string;
private accountOptionsMenuButton: string;

private hiddenAccountOptionsMenuButton: string;
private addAccountConfirmButton: string;

private pinnedIcon: string;
private addEthereumAccountButton: string;

private pinUnpinAccountButton: string;
private addSnapAccountButton: object;

private closeAccountModalButton: string;

private createAccountButton: string;

private addEthereumAccountButton: string;
private editableLabelButton: string;

private accountNameInput: string;
private editableLabelInput: string;

private addAccountConfirmButton: string;
private hideUnhideAccountButton: string;

private accountMenuButton: string;
private hiddenAccountOptionsMenuButton: string;

private editableLabelButton: string;
private hiddenAccountsList: string;

private editableLabelInput: string;
private pinUnpinAccountButton: string;

private saveAccountLabelButton: string;
private pinnedIcon: string;

private closeEditLabelButton: string;
private saveAccountLabelButton: string;

constructor(driver: Driver) {
this.driver = driver;
this.accountListItem = '.multichain-account-menu-popover__list--menu-item';
this.accountMenuButton = '[data-testid="account-list-menu-details"]';
this.accountNameInput = '#account-name';
this.accountOptionsMenuButton =
'[data-testid="account-list-item-menu-button"]';
this.hideUnhideAccountButton = '[data-testid="account-list-menu-hide"]';
this.pinUnpinAccountButton = '[data-testid="account-list-menu-pin"]';
this.hiddenAccountsList = '[data-testid="hidden-accounts-list"]';
this.pinnedIcon = '[data-testid="account-pinned-icon"]';
this.hiddenAccountOptionsMenuButton =
'.multichain-account-menu-popover__list--menu-item-hidden-account [data-testid="account-list-item-menu-button"]';
this.createAccountButton =
'[data-testid="multichain-account-menu-popover-action-button"]';
this.addEthereumAccountButton =
'[data-testid="multichain-account-menu-popover-add-account"]';
this.accountNameInput = '#account-name';
this.addAccountConfirmButton =
'[data-testid="submit-add-account-with-name"]';
this.accountMenuButton = '[data-testid="account-list-menu-details"]';
this.addEthereumAccountButton =
'[data-testid="multichain-account-menu-popover-add-account"]';
this.addSnapAccountButton = {
text: 'Add account Snap',
tag: 'button',
};
this.closeAccountModalButton = 'button[aria-label="Close"]';
this.createAccountButton =
'[data-testid="multichain-account-menu-popover-action-button"]';
this.editableLabelButton = '[data-testid="editable-label-button"]';
this.editableLabelInput = '[data-testid="editable-input"] input';
this.hideUnhideAccountButton = '[data-testid="account-list-menu-hide"]';
this.hiddenAccountOptionsMenuButton =
'.multichain-account-menu-popover__list--menu-item-hidden-account [data-testid="account-list-item-menu-button"]';
this.hiddenAccountsList = '[data-testid="hidden-accounts-list"]';
this.pinUnpinAccountButton = '[data-testid="account-list-menu-pin"]';
this.pinnedIcon = '[data-testid="account-pinned-icon"]';
this.saveAccountLabelButton = '[data-testid="save-account-label-input"]';
this.closeEditLabelButton = 'button[aria-label="Close"]';
// this selector needs to be used in combination with an account label text.
this.accountListItem = '.multichain-account-menu-popover__list--menu-item';
}

async check_pageIsLoaded(): Promise<void> {
Expand Down Expand Up @@ -100,7 +105,14 @@ class AccountListPage {
await this.driver.clickElement(this.editableLabelButton);
await this.driver.fill(this.editableLabelInput, newLabel);
await this.driver.clickElement(this.saveAccountLabelButton);
await this.driver.clickElement(this.closeEditLabelButton);
await this.driver.clickElement(this.closeAccountModalButton);
}

async closeAccountModal(): Promise<void> {
console.log(`Close account modal in account list`);
await this.driver.clickElementAndWaitToDisappear(
this.closeAccountModalButton,
);
}

async hideAccount(): Promise<void> {
Expand All @@ -110,9 +122,16 @@ class AccountListPage {

async openAccountOptionsMenu(): Promise<void> {
console.log(`Open account option menu`);
await this.driver.waitForSelector(this.accountListItem);
await this.driver.clickElement(this.accountOptionsMenuButton);
}

async openAddAccountModal(): Promise<void> {
console.log(`Open add account modal in account list`);
await this.driver.clickElement(this.createAccountButton);
await this.driver.waitForSelector(this.addEthereumAccountButton);
}

async openHiddenAccountOptions(): Promise<void> {
console.log(`Open hidden accounts options menu`);
await this.driver.clickElement(this.hiddenAccountOptionsMenuButton);
Expand All @@ -128,16 +147,6 @@ class AccountListPage {
await this.driver.clickElement(this.pinUnpinAccountButton);
}

async unhideAccount(): Promise<void> {
console.log(`Unhide account in account list`);
await this.driver.clickElement(this.hideUnhideAccountButton);
}

async unpinAccount(): Promise<void> {
console.log(`Unpin account in account list`);
await this.driver.clickElement(this.pinUnpinAccountButton);
}

async switchToAccount(expectedLabel: string): Promise<void> {
console.log(
`Switch to account with label ${expectedLabel} in account list`,
Expand All @@ -148,11 +157,16 @@ class AccountListPage {
});
}

/**
* Check account is displayed in account list.
*
* @param expectedLabel - The expected account label to be displayed in accouunt list.
*/
async unhideAccount(): Promise<void> {
console.log(`Unhide account in account list`);
await this.driver.clickElement(this.hideUnhideAccountButton);
}

async unpinAccount(): Promise<void> {
console.log(`Unpin account in account list`);
await this.driver.clickElement(this.pinUnpinAccountButton);
}

async check_accountDisplayedInAccountList(
expectedLabel: string = 'Account',
): Promise<void> {
Expand All @@ -165,6 +179,11 @@ class AccountListPage {
});
}

async check_accountIsDisplayed(): Promise<void> {
console.log(`Check that account is displayed in account list`);
await this.driver.waitForSelector(this.accountListItem);
}

async check_accountIsPinned(): Promise<void> {
console.log(`Check that account is pinned`);
await this.driver.waitForSelector(this.pinnedIcon);
Expand All @@ -175,6 +194,16 @@ class AccountListPage {
await this.driver.assertElementNotPresent(this.pinnedIcon);
}

async check_addAccountSnapButtonIsDisplayed(): Promise<void> {
console.log('Check add account snap button is displayed');
await this.driver.waitForSelector(this.addSnapAccountButton);
}

async check_addAccountSnapButtonNotPresent(): Promise<void> {
console.log('Check add account snap button is not present');
await this.driver.assertElementNotPresent(this.addSnapAccountButton);
}

async check_hiddenAccountsListExists(): Promise<void> {
console.log(`Check that hidden accounts list is displayed in account list`);
await this.driver.waitForSelector(this.hiddenAccountsList);
Expand Down
38 changes: 38 additions & 0 deletions test/e2e/page-objects/pages/experimental-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Driver } from '../../webdriver/driver';

class ExperimentalSettings {
private readonly driver: Driver;

// Locators
private readonly addAccountSnapToggle: string =
'[data-testid="add-account-snap-toggle-div"]';

private readonly experimentalPageTitle: object = {
text: 'Experimental',
css: '.h4',
};

constructor(driver: Driver) {
this.driver = driver;
}

async check_pageIsLoaded(): Promise<void> {
try {
await this.driver.waitForSelector(this.experimentalPageTitle);
} catch (e) {
console.log(
'Timeout while waiting for Experimental Settings page to be loaded',
e,
);
throw e;
}
console.log('Experimental Settings page is loaded');
}

async toggleAddAccountSnap(): Promise<void> {
console.log('Toggle Add Account Snap on experimental setting page');
await this.driver.clickElement(this.addAccountSnapToggle);
}
}

export default ExperimentalSettings;
13 changes: 11 additions & 2 deletions test/e2e/page-objects/pages/header-navbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@ class HeaderNavbar {

private lockMetaMaskButton: string;

private settingsButton: string;

constructor(driver: Driver) {
this.driver = driver;
this.accountMenuButton = '[data-testid="account-menu-icon"]';
this.accountOptionMenu = '[data-testid="account-options-menu-button"]';
this.lockMetaMaskButton = '[data-testid="global-menu-lock"]';
this.settingsButton = '[data-testid="global-menu-settings"]';
}

async lockMetaMask(): Promise<void> {
await this.driver.clickElement(this.accountOptionMenu);
await this.driver.clickElement(this.lockMetaMaskButton);
}

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

async lockMetaMask(): Promise<void> {
async openSettingsPage(): Promise<void> {
console.log('Open settings page');
await this.driver.clickElement(this.accountOptionMenu);
await this.driver.clickElement(this.lockMetaMaskButton);
await this.driver.clickElement(this.settingsButton);
}

/**
Expand Down
Loading

0 comments on commit 7054c16

Please sign in to comment.